-
• #2
Maybe a widget can indicate the timezone so we do not change the watchface?
(new Date()).getIsDST()
should tell DST on/off
and
(new Date()).getTimezoneOffset()
the zone.
Personally I would prefer having there +1 instead of the abbrevation CET (Paris)
-
• #3
But, having jetlag at this very moment, it's my objective to change my watchface. Though I grant that given the info one could add a useful widget, for those who like widgets.
To the technical point, does getIsDST actually work? Unless I'm misreading the code, GadgetBridge doesn't set it, Bangle has no manual option for it, and Espruino does not have a time zone subsystem of its own to store the rules (not that you can reverse engineer the isDST state from just an offset, anyway). Though the point that an update to GadgetBridge's time transmission could and probably should set isDST along with sending the zone is a good one.
Having an option to choose +1 rather than CET is not a bad idea, but the reason I want to go to the trouble of providing the abbreviation is that if (e.g.) you are so unfortunate as to live on a timezone boundary, or if you live on the road, you might want to know why your watch is showing an unexpected time—because your phone thinks you are now in Quebec, not because summer time just started….
-
• #5
I just read the description of widdst, and it provides a manual interface to set the DST flag. If someone found the need to write it, it kind of suggests that getIsDST() doesn't work out of the box…?
But Android has all the relevant data, whether or not it's a bug that it doesn't share it.
So my proposal is: gadgetbridge should send not just the time and the offset, but also the DST flag, the official time zone ID, and the short code for the time zone; perhaps also the next anticipated DST transition. On the Bangle, the DST flag should be updated when it is received, all these values should be persisted as the offset is already, and an event should be sent if they changed.
-
• #6
Espruino does not have a time zone subsystem of its own to store the rules
As above, Espruino does, but it's not used apart from via
widdst
- as you noted, Gadgetbridge just sets the timezone offset right now, so I don't thinkgetIsDST
will work by default.But yes, if someone wanted to make Gadgetbridge send that extra data, that'd be really handy - as long as it didn't end up breaking things for users.
However, if you wanted something right now without having to mess with Gadgetbridge and potentially Bangle.js internals then could you not look at
(new Date()).getTimezoneOffset()
in your clock face and then just look the abbreviation up in an array on the watch?I know there are different abbreviations but it seems like realistically most people would agree on the main ones like CET
-
• #7
Looking into doing it ‘right’, I find that E.setDST allows us to set a (single) DST rule prospectively—clearly useful if we are not connected at the moment of transition. There are problems, though:
- Based on a quick experiment in the emulator, it apparently updates the time and the locale but is not fully integrated with Date (it seemingly does not set IsDST or update getTimezoneOffset, the latter of which is probably bad for existing code since it seemingly means that we are out of sync with UTC, the only indication being in the toString-style accessors).
- It encodes the rules in a different way from Java's ZoneOffsetTransitionRule, and in particular doesn't seem to allow for transitions that aren't pinned to a particular day of the week (I don't know how common that is). (The documentation is also unclear on the distinction between transitions at 00:00 and at 24:00, but perhaps the behaviour is well defined and/or finessable.)
- It doesn't provide a clear strategy for the case where a rule is changing.
Is the apparent lack of integration here a reportable bug, the result of a change in design direction, or just me misunderstanding how it fits together?
In light of this, and existing code, is it better to call setDST (massaging its input so that it is at least valid for the upcoming transition, if not subsequent ones) or—my original thought—to ignore it, relying entirely on connected updates (possibly scheduling the next transition for the current zone, so that having only sporadic connection would work nicely)?
- Based on a quick experiment in the emulator, it apparently updates the time and the locale but is not fully integrated with Date (it seemingly does not set IsDST or update getTimezoneOffset, the latter of which is probably bad for existing code since it seemingly means that we are out of sync with UTC, the only indication being in the toString-style accessors).
-
• #8
Is the apparent lack of integration here a reportable bug
Yes, I guess - but if you actually mean "will I fix this" then no :) If you or someone else wanted to improve it, it'd be great though.
I think realistically it might be better to add the code to set DST into the App Loader rather than Gadgetbridge, so at least then it would work for everyone, not just Android users with Gadgetbridge.
Like many of these things, someone wanted the capability included, so they contributed the code, but then the end result (with
widdst
) was good enough for them so they never did any further integration on it.If I'm totally honest though, I think most people run their Bangle with Gadgetbridge or iPhone, in which case the time will automatically update anyway. One of the main requests I actually had was for the Bangle's timezone to update when people travelled around, and realistically
widdst
won't help with that anyway - you really need the phone connection...Scheduling one timezone change might be in interesting idea (the
sched
library would do it all, it would just need telling when to schedule), but I'm not sure if it's really worth it for the 2 times a year it happens (if disconnected from the phone) unless it's totally solid and works reliably. The last thing we need is another, different, incompatible method that only works for some users.Going back to the original point of this post though - is there really a reason you can't just have a list of timezone->name on the Bangle?
Re-architecting everything just so you can have 3 characters on your clock face seems like it might be overkill, especially (as @user140377 says) any users might prefer to just see the numerical offset, which can easily be printed right now.
-
• #9
Maybe, juste the Hanks World Clock could do ?
https://banglejs.com/apps/?q=hanks -
• #10
Oh, believe me, I understand completely about the difference between the issues that will realistically be addressed and … everything else :). I'll take a quick look at the setDST/isDST interaction and see if it feels like a hobbyable fix—though by design setDST is only a DST-aware version of manually setting the time, and may not even be part of a location-aware solution.
As to the idea of mapping from offset to name, from a technical perspective this can't work fully, because there are more timezones than there are offsets: timezones can (and do) differ in whether they observe DST and in what dates they make the change on, for example, not to mention that it might be a political hot potato to tell people in Western Australia that they are in China Standard Time or vice versa. (I had originally thought of making the watch able to display the correct time when completely disconnected, but the geospatial database would be megabyte-scale according to the back of my envelope, so not really feasible on Bangle).
For the sake of other readers I should make my own perspective clear here: I bought my Bangle because I have a long-time (if low-investment) hobby of sketching watch faces and watch features, and Bangle finally provides a low-overhead method to try out ideas. So “why don't you just use this-or-that” is not the answer for me. I'm actually trying to prototype a watch face that helps me deal with the disorientation I feel when the time changes under me, and that's what justifies my effort to myself. But of course I want to do it in a way that it might be useful to others, if there is a choice of methods, and I certainly don't want to break anything.
More generally, I think it might need saying that the experience of timezones in North America is very different from that in Europe. US states cooperate less than European nations(!) and timezone issues are a daily fact of life here; the Central/Eastern divide in the US in particular is highly populated. It's also true that while international travel has not been much of a thing for the last five years, previously (and now, again) waking up on a different continent was very much a normal experience for some of us.
Anyway, I'll try to avoid destabilising anything, and run anything with implications for others by you before trying to check them in.
Incidentally, do you have a clean way of debugging Gadgetbridge/Bangle interactions? Bluetooth seems to require running on real hardware, and there's only one Bluetooth….
-
• #11
Thanks - good point about the US states - we don't generally have much of an issue here but it must be a pain if you live near a timezone border.
It does sound a bit like the simplest solution for now might be to send
E.setTimeZone(amt, name)
instead of justE.setTimeZone(amt)
(and maybe set it in the settings too). You could then override that function in the watch face that displays it so you can get updated when there is a change.do you have a clean way of debugging Gadgetbridge/Bangle interactions?
If you connect your phone via USB then you can get the Android LogCat as part of Android Studio. If you filter it on the text
UART
it should show you what's going on, so that can be a good step.You can also go to the app loader in Gadgetbridge and then click on 'remote' and can connect the Web IDE on the desktop to it, but right now the app loader page has to be visible.
-
• #12
I think I agree.
Incidentally, although I'm confident that I saw setDST behave inconsistently with getIsDST as I described, other things later started acting strangely. I reset and reloaded my Bangle 2 and I can no longer reproduce the inconsistency—and the source seems to look right. So perhaps I had already broken something.
Oh, thanks for the LogCat pointer. It turns out that I can use that with WiFi debugging too. It's not perfect, but it's quite nice.
-
• #13
Hey, Gordon.
Without disagreeing in any way that the most direct thing to do is
E.setTimeZone(amt, name)
, I took a few more steps down the rabbit hole of looking at scheduled DST changes. It seems there are some peculiarities baked intoE.setDST()
? First, it already has 12 parameters(!), which means that unlikeE.setTimeZone()
it can't straightforwardly take optional time zone name parameters at the end. Second, to the point of not breaking existing uses, it appears from reading the code that widdst depends on the fact thatsetDST
disablessetTimeZone
to prevent further host time zone updates, and thus relies on the host not callingsetDST
, since that would break the override.So … one might wish that the entry had been
setDST(base, delta, startRule, endRule)
wherestartRule
andendRule
are arrays, for example. And while preventing automatic updates is a sensible idea, it might be better to have saidBangle.setEnableAutomaticTimezone(false)
explicitly, rather than accomplishing it by implicitly disablingsetTimeZone
.It seems wrong to set up a competing version of
setDST
, but then again it seems—if my reading of the code is correct, of course—that widdst is relying on being the sole user of the call!Given this, I can imagine several ways forward to ‘rescue’ on-device DST processing, each slightly ugly in its own way:
- Change the interface of
setDST
breakingly, updating any clients that use it, and split off an explicitsetEnableAutomaticTimezone()
entrypoint; - Keep all existing entrypoints unchanged and add a new one that allows setting DST under a slightly different interface, while (like
setTimeZone
) being implicitly disabled bysetDST
; - Extend
setTimeZone
so that it takes an optional label and optional rules to schedule changes (again while maintaining the peculiar interaction withsetDST
, now exactly as documented); - Like (2) or (3), but adding the explicit override mechanism from (1).
What, if anything, would you like for your baby? :) I'm a bit sad to give up prospective DST advice, but if your response is just to leave it and go with the minimal thing, I'll live. ;)
- Change the interface of
-
• #14
Just checked, there is also a problem, the current setDST() call does not return true on Date.getIsDST() if inside the dst time.
Also I would rename setDST() to setAutoDST() and update widdst accordingly and just add a new setDST() with just ab Integer as param. Or setDST() just checks the type of parameter. If array handle as before, if Integer just add param number of seconds to setTimeZone().
Next control the order of the setDST() calls, last one wins. So if widdst is installed chances are high that the user does want automatic dst setting and not from gadgetbridge.
-
• #15
‘rescue’ on-device DST processing
I'm not sure I understand here. What needs rescuing? I could be wrong but as I understand it, setDST actually works if it's called with the right info? Perhaps there is some issue with
getIsDST
but it looks at least likejsdGetEffectiveTimeZone
is doing sensible things internally so if there is an issue the fix might be minor.setDST might not exactly match the form you get the data on Android, but can you not convert? You mentioned not pinning day of week, but if there was somewhere that didn't do it (have we found a place?) we could update setDST in a non-breaking way by passing
-1
for the day of the week.@user140377 do you have a standalone code example (eg calling
setTime,E.setDST,Data.getIsDST
all together) I can run to have a look into this and debug it?it already has 12 parameters(!), which means that unlike E.setTimeZone() it can't straightforwardly take optional time zone name parameters at the end.
I'd argue that actually that makes it trivial to add an extra argument?
jswrap_espruino_setDST
takes an array of args, so justif (jsvGetLength(params) == 13) { ... = jsvGetArrayItem(params,12)}
would work?Having so many arguments is a bit odd, yes, but it seems it's done such that the data can be stored in a 12 element Uint16Array which ends up being relatively efficient. Not just for Bangle.js 2 where we have bags of RAM, but for other more constrained platforms that Espruino also runs on. On many devices we have a real issue with not just RAM but the size of the binary too so having some big JSON struct to parse could really hurt there.
It also makes sense if we ever did send the data from Gadgetbridge as we would be better off sending 12 numbers than a 200 byte JSON struct.
setDST disables setTimeZone
Yes, that's what the docs say. I can see why that makes sense here to keep things backwards compatible so you can install the widget alongside Gadgetbridge? If Gadgetbridge was updated to call
setDST
instead, the fact that setTimeZone was ignored wouldn't matter?widdst is relying on being the sole user of the call!
I can't think of a reason different apps should be calling setDST with different values in different places? If you were to add a setDST call to Gadgetbridge I guess we'd have to ensure users didn't have widdst installed as well or it didn't interfere, although it would still need to be available for any non-Gadgetbridge users.
So what do I think?
It seems it's fair to say you don't like the current solution - but also I think that your desire to get those 3 characters on your clock screen (which is not to everyone's taste anyway) is risking heading towards a bit of a refactoring rampage across Espruino, Gadgetbridge, the Web IDE and the App Loader, which is big enough that it could stop a non-trivial proportion of the thousands of Bangle.js users out there from having their watch tell the time properly until they update some or all software.
I really don't want to be in the position where your desire to not be told "why don't you just use this-or-that" ends up breaking possibly the most critical part of Bangle.js (telling the time) for many Bangle.js users, and I'm stuck clearing up the mess.
Of course you can do whatever you want with the code - that's the whole point - but I really don't want to merge anything back that has any risk of breaking things for existing users.
So I'd say, for this particular thing, maybe add a new
time
event type in the data sent to the watch from Gadgetbridge (https://www.espruino.com/Gadgetbridge#messages-sent-to-bangle-js-from-phone) which we could then process on the Bangle if needed (maybewiddst
could update its settings when it sees it).At some point later we could look at removing
setTime/setTimeZone
but having thetime
event gives a good platform for experimenting (and gives your clock face something to hook onto to ensure it updates automatically as soon as the event is received), and on Bangles without the software it'll just get ignored with a warning. -
• #16
[TL;DR: Your idea works, Gordon, I will do your idea (plus a separate Gadgetbridge timezone service) unless the thing at the numbered points below strikes you as better.]
Ah, I hadn't understood that setDST sidesteps the 12 parameter limit. It's documented as taking an array, but when you try it, it doesn't, it takes multiple literal arguments, and I hadn't guessed that the protocol conversion could happen under the hood, so I thought the it ws a documentation error. My ignorance, sorry.
The respect in which I means that setDST might need “rescuing” (other than the parameter count limit I falsely imagined to affect it) is the behaviour of implicitly disabling setTimeZone, which is not merely weird, it means that almost any use of it is a breaking change for someone, and you are clearly very averse to breaking changes. If Gadgetbridge starts calling it, then setTimeZone stops working after disconnection for everyone, for example.
But anyway, here's another idea:
- Give Gadgetbridge a time zone lookup service, and move the main burden to the app. This would even let people implement time zone aware alarms and calendars, for example, and would make it easier to cache your home time zone, would allow for localisation, and so on.
- [optional] Have Gadgetbridge routinely send six parameters to setTimeZone (most of which the current implementation will silently ignore, giving back compatibility):
setTimeZone(offset, [tzID], [nextChangeInstant, nextOffset, nexttzID])
. tzID here is the official identifier, not the user-facing label, giving you a key you can send to the above lookup service. The motivation for sending a single prospective transition is so that you can tolerate a few weeks of disconnection and not miss a DST change, while minimising code and space complexity on the watch. - Have a library that patches in a more sophisticated setTimeZone that uses the extra parameters, for people who want it. Maybe migrate this to the interpreter later if it's seen as good.
I suggest this not because I'm trying to argue with you—treating time updates uniformly in Gadgetbridge is attractive—but because it's a more capable variant of something you suggested yourself earlier, and I think it avoids disabling anything or changing any behaviour without anyone's consent.
On the social side, please understand that when I said I'm not interested in other people's solutions, I wasn't trying to defend being an asshole engineer, I was trying to explain (to third parties on this thread) that telling me to make do with someone else's watch face is kind of pointless because I bought a Bangle so I could write watch faces. “Why don't you have a different hobby” is sort of a non-suggestion in a way that “why don't you propose a better/less disruptive interface” or “have you overlooked such-and-such a consideration” is not.
And don't worry, I'm perfectly aware that I can fork the world, and that the only benefit of checking things in is to benefit others, and that you have stuff to do in your own life. Otherwise you'd have had pull requests some time ago.
My plan now:
I'll move the burden onto Gadgetbridge and do the time request and the timezone service approach, unless you turn out to giving setTimeZone extra args, or you tell me otherwise. It has the downside that my faces will need to disable E.setTimeZone so they can use the service instead, but as you say, that can be seen as prototyping a future, more uniform, interface.
- Give Gadgetbridge a time zone lookup service, and move the main burden to the app. This would even let people implement time zone aware alarms and calendars, for example, and would make it easier to cache your home time zone, would allow for localisation, and so on.
-
• #17
Yes, the service sounds like a good idea - then it's pretty easy to 'opt in' as you say (at least if you're using Gadgetbridge).
One reason for sending the time event, which I didn't elaborate on, is that when the Bangle is in
programmable:false
mode it doesn't accept commands on the REPL, only theGB({...})
formatted lines: https://github.com/espruino/BangleApps/blob/master/apps/boot/bootupdate.js#L46-L55So actually when programmable is off, Gadgetbridge isn't ever able to set the time! So from that point of view a time event would be a big improvement, and does at least allow us to send extra data in a more obvious format than a flat array.
Having said that, if we can actually send enough data to the Bangle to get it to always update the timezone correctly (rather that once) I feel that is a real improvement.
setTimeZone
getting disabled is a pain, but it needs to be done to make widdst work with Gadgetbridge (at the moment). However maybe a better option is to makesetTimeZone
disablesetDST
when called, and to then putsetTimeZone = function(){}
intowiddst
- having the same effect, but being reasonably easy to undo -
• #18
I have just updated cutting edge Espruino to make setTimeZone overwrite setDST, and vice versa - I'll update widdst now as well
-
• #19
I see—so the GB() idiom is clearly preferred going forward, and it's acceptable, nay desired, to have time setting move there eventually. I had wondered about that, and it turns out to be the key point :).
Oh, about the Gadgetbridge repo—if I submit Bangle-related changes over there do you get pinged to review them, or is it safest for all if I run them by you in advance on a side channel, or what? I'm not sure how they are set up.
-
• #20
about the Gadgetbridge repo
They don't go to me automatically, so it might be worth it pinging me about them before or when you submit them - thanks! I'll be off over Christmas though so I'm unlikely to respond for the next 2 weeks I'm afraid
-
• #21
I'll do that. Enjoy your holiday (I hope that's what's happening)!
-
• #22
I was actually looking into this as well, I wanted to have a widget that showed the time at home when you're in a different timezone. I could be wrong, but using offsets seems to be a bad idea for my application since your "local" offset could change depending on if DST is in effect. If there's any way to get more comprehensive tz info accessible via the API, I'd love to help out!
-
• #23
Hey chroma. How were you planning on identifying “home”? I've been able to think of at least four methods, each with drawbacks:
- explicit picking from a list, which is more challenging than it initially appears;
- waiting until you're in your home zone and then saying, here, this one now, which is more limiting than some might imagine (though technically savvy users could I suppose force the issue by temporarily overriding their phone's time zone selection);
- having a picker for over on Gadgetbridge and then pulling the results over to the watch, which involves messing a lot with GB;
- logging user behaviour and taking the one that's used most over an (eventually) extended period.
The last is both the hardest and the most magical (though I note there's a security implication if your watch is seized)…
- explicit picking from a list, which is more challenging than it initially appears;
-
• #24
oh I was planning on having the user pick their home timezone from the tz database, ie America/Los_Angeles
-
• #25
You could add that to mylocation
My Bangle correctly follows my phone into a new time zone when appropriate, but I'd like my watch face to be aware of it—adding a small note (e.g. "PDT") on the display and perhaps a shadow hour hand for the pre-change time (if DST) or home time (otherwise).
To a first approximation, the phone appears to have this information in its TimeZone object, and could pass it through GadgetBridge into setting.json at transmitTime, perhaps also firing a Bangle.event("timezone").
There are a couple of wrinkles I know of: first, the familiar short timezone names are informally documented as deprecated because they are ambiguous for input (the deprecation may not be intended to apply to their use as display names but the wording in the documentation doesn't seem clear); and second that Java's TimeZone.getDisplayName() takes a Locale—which will obviously make a difference for long names like “Pacific Standard Time”, but the three letter abbreviations? I dunno. In any case, using the host's UI locale is technically incorrect given that the Bangle could presumably be set differently. But I imagine both these worries could be ignored in reality.
Also, of course, there may be considerations I've never imagined.
Or the info might already be available and I've been looking in the wrong place.
(More broadly it would be nice to get the current time zone's DST data uploaded to the watch, because it would be nice to get notice of DST *in advance*—as long as the world is still using them. Even more broadly it would be nice to get calendar events right in respect of time zones, but that's clearly an undertaking of another order.)
What does anyone think?