Daylight Saving Time Mismatch between Simulator and Device

We live in UTC+1:00 Timezone.

In Summer (lfrom Mar 27 to Okt 30) comes additional hour to this ("Summertime").

The Simulator taks care of summer or wintertime and get local time correct. On Device the getting of a time from the other timearea become 1 hour defference, which is wrong:

  • If now is summer and I want to get correct Gregorian representation of a epoch from winter, it diffs 1 hour from correct time
  • If now is winter and I want to get correct Gregorian representation of a epoch from summer, it diffs 1 hour from correct time

Consider the following Code based on Unix Epochs:

    function test_time(dc as G.Dc) {
        var location = new Position.Location({
                                            :latitude  =>  51.338248,
                                            :longitude => 12.362537,
                                            :format    => :degrees,
                                        });
        var text = "";
        // var m = Time.Gregorian.moment({ :year => 2015, :month => 10, :day => 15, :hour => 14, :minute => 0, :second => 0 });
        // var i = Time.Gregorian.info(m, Time.FORMAT_MEDIUM);
        // var s = Lang.format("$1$ $2$ $3$ $4$:$5$:$6$", [i.month, i.day, i.year, i.hour.format("%02d"), i.min.format("%02d"), i.sec.format("%02d")]);
        // Sys.println(s);

        // m = Time.Gregorian.moment({ :year => 2015, :month => 11, :day => 15, :hour => 14, :minute => 0, :second => 0 });
        // i = Time.Gregorian.info(m, Time.FORMAT_MEDIUM);
        // Sys.println(Lang.format("$1$ $2$ $3$ $4$:$5$:$6$", [i.month, i.day, i.year, i.hour.format("%02d"), i.min.format("%02d"), i.sec.format("%02d")])); 
    
        // Sys.println("");

        Sys.println("\n *************** test_time *****************");
        Sys.println("summer=1667023200");
        Sys.println("  should GMT: Sat Oct 29 2022 06:00:00 GMT+0000");
        Sys.println("  should loc: Sat Oct 29 2022 08:00:00 GMT+0200 (Mitteleuropäische Sommerzeit)");
        var m = new Time.Moment(1667023200);
        var i = Time.Gregorian.utcInfo(m, Time.FORMAT_SHORT);
        var s = Lang.format("utc: $1$ $2$ $3$ $4$:$5$:$6$", [i.month, i.day, i.year, i.hour.format("%02d"), i.min.format("%02d"), i.sec.format("%02d")]);
        text += s;
        Sys.println(s); 
        i = Time.Gregorian.info(m, Time.FORMAT_SHORT);
        s = Lang.format("inf: $1$ $2$ $3$ $4$:$5$:$6$", [i.month, i.day, i.year, i.hour.format("%02d"), i.min.format("%02d"), i.sec.format("%02d")]);
        text += "\n" + s;
        Sys.println(s);

        text += "\n";

    
        Sys.println("");
        Sys.println("winter=1667113200");
        Sys.println("  should GMT: Sun Oct 30 2022 07:00:00 GMT+0000");
        Sys.println("  should loc: Sun Oct 30 2022 08:00:00 GMT+0100 (Mitteleuropäische Normalzeit)");
        m = new Time.Moment(1667113200);
        i = Time.Gregorian.utcInfo(m, Time.FORMAT_SHORT);
        s = Lang.format("utc: $1$ $2$ $3$ $4$:$5$:$6$", [i.month, i.day, i.year, i.hour.format("%02d"), i.min.format("%02d"), i.sec.format("%02d")]);
        text += "\n" + s;
        Sys.println(s); 
        i = Time.Gregorian.info(m, Time.FORMAT_SHORT);
        s = Lang.format("inf: $1$ $2$ $3$ $4$:$5$:$6$", [i.month, i.day, i.year, i.hour.format("%02d"), i.min.format("%02d"), i.sec.format("%02d")]);
        text += "\n" + s;
        Sys.println(s);  

        
        
        var myTextArea = new WatchUi.TextArea({
            :text=>text,
            :color=>Graphics.COLOR_BLACK,
            :font=>[Graphics.FONT_MEDIUM, Graphics.FONT_XTINY, Graphics.FONT_XTINY],
            :locX =>WatchUi.LAYOUT_HALIGN_CENTER,
            :locY=>WatchUi.LAYOUT_VALIGN_CENTER,
            :width=>220,
            :height=>200
        });
        myTextArea.draw(dc);
    }

on Log it prints:

Info: Okt 29 ist the last day of summertime (UTC+2:00), Okt 30 ist the first day with wintertime (UTC+1:00)

*************** test_time *****************
summer=1667023200
  should GMT: Sat Oct 29 2022 06:00:00 GMT+0000
  should loc: Sat Oct 29 2022 08:00:00 GMT+0200 (Mitteleuropäische Sommerzeit)
utc: 10 29 2022 06:00:00
inf: 10 29 2022 08:00:00

winter=1667113200
  should GMT: Sun Oct 30 2022 07:00:00 GMT+0000
  should loc: Sun Oct 30 2022 08:00:00 GMT+0100 (Mitteleuropäische Normalzeit)
utc: 10 30 2022 07:00:00
inf: 10 30 2022 08:00:00

Here the Screenshot of Simulator and real Device:

It is so that the simulator takes into account the correct time zone offset of the given time:

  • in summer 2 hours are added (UTC+2:00)
  • in winter 1 hour is added (UTC+1:00)


But the same call on the clock ALWAYS takes into account the CURRENT timezone offset at runtime of the program.

I can correct this manually in the codee, but this leads to a wrong display in the simulator and works only for Central European Time. The problem cannot be solved satisfactorily for mic. Since my watchface represents the course of the sun and additionally the shortest and longest day, it is always displayed incorrectly, since times must always be calculated from the other time (summer and winter).

This then looks like this:

The simulator calculates (we are NOW in summer time UTC+2:00) the sunrises and sunsets for winter (blue line with UTC+1:00) correctly.

The real device calculates the times in coming winter with the TODAY SHIFT (UTC+2:00) and therefore wrong.

How can I solve the Probelem? To me it looks like a bug in the API.

  • After tests... this code:

    	var 
    	ct = SYS.getClockTime(),
    	nw = TIM.now().value();
    	lg(ct.hour + ":" + ct.min +  " DST=" + (ct.dst / 3600) + " TZO=" + (ct.timeZoneOffset / 3600));
    	lg("epoch now: " + nw);
    //	nw = 1664034368
    //	 
    //	Assuming that this timestamp is in seconds:
    //	GMT: Saturday, 24 September 2022 15:46:08
    //	Your time zone: Saturday, 24 September 2022 17:46:08 GMT+02:00 DST
    //	Relative: A few seconds ago 
    //	
    //	POLAND CET UTC+1, CEST (summer time) UTC+2 (CET +DST), it means  
    //	ct.timeZoneOffset = CEST = CET + ct.DST 
    	
    	nw = 1664034368;//"const" for test 
    	lgGregorianInfo("now", nw);
    	
    	//! adding 60 days it will be CET
    	var cetN = nw + (60 *24 * 60 *60); 
    	GcetN = lgGregorianInfo("cetN", cetN);
    	lg(GcetN);	
    	
    	//! adding 360 days it will be CEST next year
    	var cestN = nw + (360 *24 * 60 *60); 
    	GcestN = lgGregorianInfo("cestN", cestN);
    	lg(GcestN);
    	
    	//! subtracting 270 days it will be CET
    	var cetP = nw - (270 *24 * 60 *60); 
    	GcetP = lgGregorianInfo("cetP", cetP);
    	lg(GcetP);
    	
    	//! subtracting 360 days it will be CEST next year
    	var cestP = nw - (360 *24 * 60 *60); 
    	GcestP = lgGregorianInfo("cestP", cestP); 
    	lg(GcestP);
    }
    var
    GcetN,
    GcestN,
    GcetP,
    GcestP;
    
    function lgGregorianInfo(str, epoch)
    {
    	epoch = GRE.info(new TIM.Moment(epoch), 0);
    	var ret = 
    		epoch.hour.format(FORMAT_02D)	+ ":" +
    		epoch.min.format(FORMAT_02D);
    	lg(
    		str								+ ": "+
    		epoch.year						+ "-" + 
    		epoch.month.format(FORMAT_02D)	+ "-" +
    		epoch.day.format(FORMAT_02D) 	+ " " +
    		ret);
    	return ret;
    }

    On sim/console shows good values:

    >now: 2022-09-24 17:46<
    >cetN: 2022-11-23 16:46<
    >16:46<
    >cestN: 2023-09-19 17:46<
    >17:46<
    >cetP: 2021-12-28 16:46<
    >16:46<
    >cestP: 2021-09-29 17:46<
    >17:46<

    On device all values are 17:46 - it means (as I thought) api use "current" utcoffset/dst parameters instead of this from "destination day".

    So bug Slight smile. And all apps showing any future/past (e.g. next sun event the day before time changing) will show bad time.

    BTW. The meaning "I don't know how it works" is - I can see results (good or bad) but don't know how it has been realised.

  • On device all values are 17:46 - it means (as I thought) api use "current" utcoffset/dst parameters instead of this from "destination day".

    So bug

    And all apps showing any future/past (e.g. next sun event the day before time changing) will show bad time.

    Yeah, that's what both OP and me said. It's cool that you verified it for yourself (I'm 100% in favor of never blindly trusting what anyone else says, even the Garmin docs), but idk what we were arguing about if we came to the same conclusion in the end.

    All I asked was that if you want to see it fixed, please upvote the bug report.

    BTW. The meaning "I don't know how it works" is - I can see results (good or bad) but don't know how it has been realised.

    Nobody knows for sure, but we can make an educated guess, based on how other systems work and how Garmin appears to work.

    Like we can guess that because your Garmin device has enough information to display the time and adjust for DST / different time zones without an internet connection (*), then the TZ/DST information must be stored locally. Further anecdotal evidence for having locally stored TZ/DST information is what you posted about the TZ map and DST information being updated through Garmin Express. Additional evidence is the existence of Gregorian.localMoment(), which takes UTC time and gives you local time for any given location (no internet connection should be required here).

    If all the TZ/DST information is local, then it should be an easy fix.

    (* I'm sure it works offline for DST transitions, and 99% sure it works for moving between different time zones.)

  • I haven't verified only but tried to understand some things (because not good docs) to show users good values (and not only get some data, put into api func and ends work).

    I'll check soon if watch change time to CET and I will be really surprised if it will run Slight smile especial after reading user manual

  • Yes, I agree the docs are bad. I complain about this all the time (I complain about a lot of things tho).

    I'll check soon if watch change time to CET and I will be really surprised if it will run

    Notice that the manual says "acquire satellites" OR "open [Garmin Connect] on your paired phone". It should be enough to get a GPS fix, without having an internet connection.

    But yeah, this is something you can check for yourself (which is the best way to settle any disagreement imo, when possible).

    If I'm wrong, I'm wrong. But I've had Garmin watches for a few years, and I always turn off bluetooth by default (I only sync after activities). I'm 100% sure I would've noticed if the time was wrong when DST starts or stops.

    As a matter of fact, I think you should see the watch adjust for DST instantly (at 2 AM, 3 AM, or whenever DST stops). No need for a GPS fix or a phone sync, because all the information the watch needs should already be locally available.

    Not to state the obvious, but nobody is going design a clock on a computing device where the time could be off by one whole hour if you're not constantly connected to the internet. Customers would complain. It's especially the case with a Garmin watch, which is designed so you don't need a constant connection to the internet.

    (OTOH, a few seconds drift would be expected and normal.)

  • Another test you can do, if you have a CIQ 3.3 device, is use Gregorian.localMoment(). Build an app that shows local time from different cities around the world, based on a fixed UTC moment. Sideload the app and run it without an internet connection (turn off bluetooth and wi-fi).

    You will have to ask yourself how the watch can convert from UTC time to local time all over the world, unless it has a locally stored TZ/DST database.

    You can even use Arizona and Denver as two locations to test. This is a good test because Arizona is on MST (UTC-7) all year round, while Denver is currently on MDT (UTC-6).

    So if you test with 2022-09-24 0:00 UTC (for example):

    - Arizona should be 2022-09-23 17:00

    - Denver should be 2022-09-23 18:00

    If the watch can correctly do those conversions without connecting to the internet, it means that it has all the zone offsets and DST rules stored locally.