Wrong locale time in Android webview - javascript

We have an android app which more or less constists of only a webview, displaying a webapp. At some point, we want to display some timestamp to the user and use the following code
let dateString = new Date().toLocaleTimeString();
The problem is, this always produces a 12h time representation (ie with AM/PM, for instance 3:15 PM instead of 15:15), even if we set the device to use 24h clock (and the device does respect this setting, because we see the correct time in the statusbar). We also set the device to use German language and removed the english language settings. No luck either. The device locale is used, as our app is shown in the correct language, but the timestrings are still wrong.
window.navigator.language -> "de-AT"
new Date().toLocaleTimeString(); -> "6:10:13 PM"
new Date().toLocaleTimeString(window.navigator.language); -> "18:10:32"
At a first glance, the last line seemed as a possible workaround, but if language is for instance "en-US" this will always return 12h format, regardless whether the os is configured for using 24h clock or not.
I'm aware of this question, which seems somehow related to this issue, but the answers don't work for us.
Using various virtual devices I found out, it still works correctly on chrome 58 (on API 26) but does not work any more on chrome 61 (on API 27). Currently testing at chrome 70 beta, the problem still persists.
It's also important to note, that it works correctly in chrome itself (ie if I display a time stamp in a test website, it uses the system settings), but only fails in the webview.
Our current workaround is to generate timestamps via native app, but that seems rather clumsy.
UPDATE
I know I can do some things on the webapp side like overriding toLocaleTimeString (which in fact, I already did to get the correct format from the native app).
My question is more about: Is there any known changed behaviour (and maybe a setting to reverse this) in the native WebView which causes this issue to happen, because this works/worked like a charm on devices with Chrome <= 58.
In the meantime I filed an issue with chromium, they were able to reproduce it. Maybe this will get fixed in one of the next versions ...
UPDATE 2
The issue is already fixed on google's side and may be rolled out with some future update. In the meantime I will continue to use my current override of toLocaleTimeString() which calls back to the native app ...

Please try overriding the function Date.prototype.toLocaleTimeString
let f = Date.prototype.toLocalString;
Date.prototype.toLocalString = function(){
let s = f();
//since the method fails for US-locale manupulate s as required when locale is US and system is set to show 24 hr
// if ends with AM - just delete "AM"
// if ends with PM - add 12 to hr and delete "PM"
return s;
}
---please avoid directly modifying prototype of inbuilt JS objects---
let me also point out that it is not advisable to play with inbuilt object of JS as it may break or interrupt execution of some external libraries. So better create a new object and modify prototype of that object - use prototypical inheritance.

Related

JS offset of a date on specific browser/configuration

I've an API that returns birthdate as 2020-11-24T00:00:00 to a React app. That react app uses that portion to display it:
new Date("2020-11-24T00:00:00").toLocaleDateString();
The issue is that on my browser and all browsers I have seems to give no issue: date is correclty shown. One or two customer complains about ir because they see the date 23/11/2020 (one dat before). I cannot reproduce the bug.
As I understood, Dates can be interpreted by browser as Zulu date so browser can translate the date from GMT+0 to browser's region. Right. Now I have to try to reproduce the bug in order to fix it and And I simply cannot because of misunderstanding.
First postulate: Date("2020-11-24T00:00:00") is going wrong, let's try that: fiddle => no, I cannot reproduce with my browser
Second postulate: .toLocaleDateString() is going wrong, let's try that: [fiddle][2] => no, I cannot reproduce with my browser when changing Location in Chrome.
How can I reproduce the issue in order to fix it?
The current ECMAScript standard obliges your example date string to be interpreted as a local date/time, because the time part is included and the timezone part is not.
Through time the rules for parsing strings have become a bit more standardized, but older JavaScript engines may behave differently. See for instance a post from 2013 or 2017 where a difference was reported (at that time). It is likely that some of your users run that JavaScript on much older engines which interpret this type of date string as UTC (as if suffixed by "Z").
Mozilla Contributors write about using Date.parse (which uses the same parser as when the string is passed to the constructor):
Note: Parsing of strings with Date.parse is strongly discouraged due to browser differences and inconsistencies.
To remove all ambiguity also for older browsers, parse the string yourself:
var dateStr = "2020-11-24T00:00:00";
var dateArr = dateStr.match(/\d+/g);
var date = new Date(dateArr[0], dateArr[1]-1, dateArr[2]); // Add time parts if needed
// Guaranteed to be reporting on the 24th of November 2020 (in locale format)
console.log(date.toLocaleDateString());

Strange timezone "Etc/GMT-1" in firefox

Since today I noticed a problem in one of the webapps running on my server, in that it reported the timezone wrong - but only on Firefox
And indeed, checking in the console:
Intl.DateTimeFormat().resolvedOptions().timeZone
delivers
Etc/GMT-1
What's this supposed to mean? I am in a GMT+1 (+2 when counting currently active daylight savings time) timezone.
On Chrome, the above command correctly returns Europe/Vienna.
OS: Windows 10, Firefox version: 59.0.2 (64 bit) with Add-Ons: NoScript, PrivacyBadger, uBlock Origin (tried with add-ons deactivated, no change), Chrome 65.0.3325.181 (64-Bit)
Is this a Firefox bug (though as far as I can tell Firefox was not updated in the past few days, and the problem only started today)?
In the webapp also nothing changed as far as I can tell, so I suspect this to be a Firefox issue somehow (though I have no idea how Firefox gets to this "wrong" timezone info). Or is the timezone actually correctly referencing a time 2 hours ahead of UTC? And is the webapp mabye wrong in not recognizing this properly? I'm fresh out of ideas.
Searching in google hasn't brought up any recent hints regarding this (only some outdated ones, e.g.: Incorrect timezone in Firefox, compared to Safari, using javascript Date(), https://support.mozilla.org/de/questions/1191823). I also can't find anything else pointing at unusual timezone readings currently in Firefox, so I'm really wondering where this is coming from!
UPDATE:
To be honest, I have no idea what timezone firefox reported before - the actual problem I have is that the webapp (owncloud), when run in Firefox, reports not knowing the time zone specification - the message translates to "unknown timezone specification Etc/GMT-1. Falling back to UTC". The times are then 2 hours behind what I would expect them to be (which makes sense as current europe/vienna or europe/berlin time is 2 hours ahead of UTC). Not knowing how to interpret Etc/GMT-1 might be an issue on owncloud's part, but it has worked up until a few days ago, and still continues to work on Chrome...
More info as requested by Matt Johnson below:
>tzutil /g
W. Europe Standard Time
>echo %TZ%
%TZ%
Registry:
I suppose I get Europe/Vienna instead of Europe/Berlin since I have configured to be located in Austria?
Addendum: I only get this behavior so far only on a single Windows 10 machine. On another Linux machine running Firefox, I do not see this behavior. I yet have to check on another Windows 10 machine
There are two separate questions here:
Etc/GMT-1
What's this supposed to mean? I am in a GMT+1 ...
The tz database identifiers of the form Etc/GMT±* deliberately have an inverted sign than the usual forms we expect under ISO 8601. That is, they are in terms of positive values being West of GMT, rather than positive values being East of GMT. This is covered both in the Wikipedia article on the tz database, and in the commentary in the tz database itself.
Thus, it does indeed align with the current GMT+1 offset of your time zone. However, it doesn't reflect any DST of GMT+2.
... On Chrome, the above command correctly returns Europe/Vienna.
... Is this a Firefox bug?
That depends. What is your OS time zone set to? You said you are running Windows 10. Though Europe/Vienna maps to W. Europe Standard Time, the primary (001) mapping for that would be Europe/Berlin - so it's a bit odd that you would get Europe/Vienna unless there is something else influencing the result.
It is possible you have stumbled upon a bug, but it's also possible you have customized or corrupted time zone settings on your OS.
Please supply (via edit of your question) the values of:
The output of the tzutil /g command on the command line.
The value (if any) of a TZ environment variable (echo %TZ%)
All the values under this registry key (screenshot would be best):
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation
That will help identify the problem further. I will edit my answer accordingly. Thanks.

Can Javascript's Date().getTime() be incorrect if the system clock is wrong?

Occasionally we get a report from users that a countdown timer in our application is set too high, or is displaying negative numbers. I figured it was a timezone issue but I've been unable to replicate it by changing my clock settings - even if I change the ones in my computer's BIOS. The only way I was able to replicate it was by actually adding or subtracting 3600 seconds from the timestamp in my code.
I read that Date().getTime() always provides a UTC timestamp. I doubt it's sourced from the internet, so it makes sense to me that it could be wrong if the computer is providing an incorrect time. Unfortunately I can't make this actually happen yet.
I also can't find much information online about this, or gather any data from our users (This is very infrequent and they unsurprisingly become unwilling to help once I mention timezones and system clocks).
So, can Javascript provide me with an incorrect UTC timestamp? If so, is there a strategy to ensure the timestamp is accurate?
Edit -
It struck me that the simplest solution is probably just to send over the server time and use that, since it doesn't need to be especially accurate.
I'd say if you want to dig into implementation - go and check Mozilla code. It's open source. I dig into it because of your post and I finally reach to the file: https://hg.mozilla.org/mozilla-central/file/tip/js/src/vm/Time.cpp
It has some interesting function called NowCalibrate(). Seems that it's doing the trick with fixing time if the system time is off or something.
You can check lines 79 to 140. I will attach portion of the code that initializes the time.
void
PRMJ_NowInit()
{
memset(&calibration, 0, sizeof(calibration));
// According to the documentation, QueryPerformanceFrequency will never
// return false or return a non-zero frequency on systems that run
// Windows XP or later. Also, the frequency is fixed so we only have to
// query it once.
LARGE_INTEGER liFreq;
DebugOnly<BOOL> res = QueryPerformanceFrequency(&liFreq);
MOZ_ASSERT(res);
calibration.freq = double(liFreq.QuadPart);
MOZ_ASSERT(calibration.freq > 0.0);
InitializeCriticalSectionAndSpinCount(&calibration.data_lock, DataLockSpinCount);
// Windows 8 has a new API function we can use.
if (HMODULE h = GetModuleHandle("kernel32.dll")) {
pGetSystemTimePreciseAsFileTime =
(void (WINAPI*)(LPFILETIME))GetProcAddress(h, "GetSystemTimePreciseAsFileTime");
}
}
On the other side I found that every other browser has it's own implementation, but Mozilla can be a real world example of how the initial time can be calculated and calibrated after certain period.

Chrome timeZone option to Date.toLocaleString()

I have recently discovered that there is a new extension to JavaScript. This adds several features to the Date object in the toLocaleString, toLocaleDateString and toLocaleTimeString functions. Reference here.
I am particularly interested in the timeZone option, that supports IANA/Olson time zones, such as America/New_York or Europe/London. This is currently only supported in Google Chrome.
Previous advice was that to work in JavaScript with any other time zone than UTC or your own local time zone, one had to use a library. But now, it appears that this is starting to be incorporated directly into the browser. So now you can do this:
new Date().toLocaleString("en-US", {timeZone: "America/New_York"})
// output: "7/4/2013 5:15:45 PM"
Or:
new Date().toLocaleString("en-NZ", {timeZone: "Pacific/Chatham",
timeZoneName: "long"})
// output: "7/5/2013 9:59:52 AM GMT+12:45"
Or:
new Date().toLocaleString("en-GB", {timeZone: "Europe/London",
timeZoneName: "short"})
// output: "4/7/2013 22:18:57 United Kingdom Time"
// (strange time zone name, but ok)
This is very cool, but I have a few questions:
Is this part of a new standard? Perhaps buried somewhere in ECMAScript 6? Or is it just something custom to Chrome?
Why just Google Chrome? Is it supported anywhere else? Are there plans to supported it anywhere else?
I checked node.js, which uses Chrome's JavaScript runtime, but it doesn't work there. Why not?
Is the time zone data accessible in any other way than the functions I listed? If only available when formatting strings, then doing any calculations based on the results may be difficult.
This is focused on output, but how would I use it for input? Is there a way to pass the time zone in the constructor to the Date object? I tried the following:
// parsing it with a date and time
new Date("2013-01-01 12:34:56 America/New_York")
// passing it as a named option
new Date(2013,0,1,12,34,56,{timeZone:"America/New_York"})
Neither worked. I couldn't find anything in the specs, so I don't think this exists (yet), but please tell me if I am wrong.
The issue described in this post, created by a flaw in the ECMAScript 5 spec, still affects the output, even when the correct data is in the TZDB. How is it that both old and new implementations are coexisting? One would think it would be all the old way, or all the new way. For example, with my computer's time zone set to US Eastern Time:
new Date(2004,10,7,0,0).toLocaleString("en-US",{timeZone:"America/New_York"})
returns "11/6/2004 11:00:00 PM". It should return midnight, since I started at midnight and my local time zone matches the output time zone. But it places the provided input date at the wrong UTC point due to the ES5 issue.
Can I expect that as IANA releases updates to the TZDB that Google will push Chrome updates that contain the changes?
update
There is pretty extensive write-up about the API here
Is this part of a new standard? Perhaps buried somewhere in ECMAScript
6? Or is it just something custom to Chrome?
Yes, these are part of the ECMAScript Internationalization API. It is implemented separate from ECMAScript, but the requirement of implementing ECMAScript Internationalization API is to first have correct implementation of ECMAScript 5.1
Why just Google Chrome? Is it supported anywhere else? Are there plans
to supported it anywhere else?
For the recent years, Google Chrome has mostly been first to implement new features. Mozilla is more conservative, still for example discussing whether to implement the download attribute of a elements. It is now available in IE11 Beta and Opera too. It will be available in Firefox 25.
I checked node.js, which uses Chrome's JavaScript runtime, but it
doesn't work there. Why not?
node.js just uses the same engine, which is a separate project from the Google Chrome browser. The engine just implements Ecmascript 5.1. This is an extension node.js would have to implement separately right now. It will become available in V8 in Q3 so probably a bit after that you can use it in node.js.
This is focused on output, but how would I use it for input? Is there
a way to pass the time zone in the constructor to the Date object? I
tried the following:
There is nothing about inputting dates in the specification. I personally cannot see how this would be useful, you are doing it wrong if you are not transmitting UTC timestamps because something like "2013-01-01 12:34:56 America/New_York" is ambiguous during transitions from DST to standard time.
The issue described in this post, created by a flaw in the ECMAScript
5 spec, still affects the output, even when the correct data is in the
TZDB.
This is input issue, not output. Again, constructing a date with local timezone that you cannot influence or detect is doing it wrong. Use the timestamp constructor overload or Date.UTC.
Can I expect that as IANA releases updates to the TZDB that Google
will push Chrome updates that contain the changes?
Nothing in the spec but I think it will be reasonable to expect that the rules are not too much behind.
Is this part of a new standard? Perhaps buried somewhere in ECMAScript
6? Or is it just something custom to Chrome?
Indeed it is part of the new ECMA-402 standard. The standard is very difficult to read, but there is this friendly introduction.
Why just Google Chrome? Is it supported anywhere else? Are there plans
to supported it anywhere else?
MDN has a list of supporting browsers. According to Bug 853301 it will be available in Firefox 25.
I checked node.js, which uses Chrome's JavaScript runtime, but it doesn't work there. Why not?
Possible reasons are many; it is not up to the current code base, or it would make the node.js bigger and slower (the previous bug tracker entry from Mozilla indicates that the timezone data increased the download size of Firefox by 10 %, and caused the I/O to increase substantially during browser start up.
Is the time zone data accessible in any other way than the functions I listed? If only
available when formatting strings, then doing any calculations based on the results may be
difficult.
Seems that it is not available. Furthermore, the Intl API primer talks that only UTC and local time zone are absolutely required to be supported.
This is focused on output, but how would I use it for input? Is there a way to pass
the time zone in the constructor to the Date object? I tried the following:
The Intl API only speaks about date/time formatting, string collations and number formatting. The datetime formatting not only supports the Gregorian calendar but also many kinds of other calendars, lunar, lunisolar and so forth.
The issue described in this post, created by a flaw in the ECMAScript 5 spec, still affects
the output, even when the correct data is in the TZDB. How is it that both old and new
implementations are coexisting? One would think it would be all the old way, or all the new
way. For example, with my computer's time zone set to US Eastern Time:
new Date(2004,10,7,0,0).toLocaleString("en-US",{timeZone:"America/New_York"})
returns "11/6/2004 11:00:00 PM". It should return midnight, since I started at midnight and > my local time zone matches the output time zone. But it places the provided input date at the > wrong UTC point due to the ES5 issue.
The reason for that is that ES5 mandates that the input to new Date be calculated using the current DST and offset, that is it is America/New York but with EDT timezone, even though Nov 6 is not in EDT. Obviously as this is so specified, then it cannot be changed. However, as Chrome is using the TZDB to do the conversion from the bare UTC point-in-time value to the America/New York tz, it then does consider the time as being in EST.
Can I expect that as IANA releases updates to the TZDB that Google will push Chrome updates
that contain the changes?
I'd believe so

Why would I use input type date in a regular web application?

I am currently building an iOS App using Phonegap and jQuery Mobile, and I am using the HTML5 input type date element, for the user to enter their birthday. This makes total sense for a Phonegap app to use, because this invokes the phones native datepicker - thats great!
However, when using a desktop browser to test out my app (I am using Firefox 16), no datepicker is shown, obviously due to the fact that Firefox has not implemented this yet. Thats cool, except for the fact that I can enter whatever I want into that field - any format I want.
The specification says that it should use the yyyy-mm-dd format, and my iOS device does that. But, if I enter e.g 06-09-94, that won't play nicely when I try to convert it to a JavaScript Date Object.
Here are my tests:
// Value is set to 1994-09-06
console.log(new Date($("input[type=date]")[0].value));
// > Date object representing September 6, 1994
// Value is set to 06-09-1994
console.log(new Date($("input[type=date]")[0].value));
// > Invalid date
I've read somewhere that internally (atleast for chrome), all "valid" date formats will be represented as yyyy-mm-dd, but that's not happening. http://updates.html5rocks.com/2012/08/Quick-FAQs-on-input-type-date-in-Google-Chrome
What kind of format does the input value return?
The input.value always returns as yyyy-mm-dd regardless of the
presentation format.
So, as the title suggests: Why would I want to use an <input type="date"> in a non-mobile based web application?
You would want to use the HTML 5 form elements because there is hope that one day all major browser support them. As you noticed, datepickers etc offer really great functionality out of the box, which would have to be coded by hand otherwise.
You wouldn't want to just let them stand on their own, though, in environments where you have no controller over what browser the user is using.
The suggested way to handle this at the moment is to check, if the browser supports the required form field, if not, use an alternative like a jQuery UI element.
Quickly googled tutorial: http://www.javascriptkit.com/javatutors/createelementcheck2.shtml
I think, some of the js framework handle such a check internally, at least for dojo I know that they try to use as much native browser functions as possible - but I'm not sure how it is handled with datepickers.
I have to say, I'm surprised firefox hasn't implemted this yet, Chrome and Opera seem to lead the way....
Since not all browsers still support all HTML5 features you can't see all it's benefits right away.
The <input type=date> tag is one of those features, if you run the code in the Chrome browser it will open date picker, when you can choose the date by clicking on the arrow button. This feature is missing though on Safari browser. Just wait for updates of browsers.
You check that the value is in an appropriate date format before trying to convert it to a Date object.
If it isn't, display an error message to the user instead (e.g. by adding a <label> by the <input> or using an alert).

Categories

Resources