dayjs
dayjs copied to clipboard
The timezone plugin is not compatible with React Native Hermes engine
The timezone plugin throws a timezone RangeError error when calling .tz because React Native Hermes engine doesn't has support for Intl timezones and toLocaleString
This two lines are the ones that are throwing the error
https://github.com/iamkun/dayjs/blob/dev/src/plugin/timezone/index.js#L20
https://github.com/iamkun/dayjs/blob/dev/src/plugin/timezone/index.js#L98
Could be posible to update the plugin to not depend on Intl and toLocaleString('en-US', { timeZone: timezone }) ?
I've been facing the same issue today.
With the formatjs polyfill that's recommended, I've got it partially working:
dayjs.tz("2013-11-18 11:55", "America/Toronto")
returns correctly with "2013-11-18T16:55:00.000Z"
However, it's not fully working:
dayjs("2013-11-18 11:55").tz("America/Toronto")
returns null
So, with a polyfill I'm able to get Parsing in Zone to work, but not Converting to Zone.
@sohcah what polyfill setup are you using? I tried to use formatjs but I didn't managed to make it work
@ellipticaldoor
I created an empty polyfill.ts
file, as the polyfill isn't required on iOS or Web, and created a polyfill.android.ts
file which contains the following: https://gist.github.com/sohcah/c223886c3e40a7b9c0283cd0213e2a54
I've got a few console logs in there to test Converting and Parsing, giving me this output:
Then I'm just putting import './polyfill.ts';
in my App.tsx
file.
My converting issue seems similar to #1356... so possibly related?
Looks like Date.toLocaleString()
works differently with the Polyfill.
Running new Date().toLocaleString("en-US", { timeZone: "America/Toronto" })
in a browser returns "2/9/2021, 9:03:52 AM"
, whilst with the polyfill it returns 2/9/2021
.
// @ts-ignore
Date.prototype._toLocaleString = Date.prototype.toLocaleString;
// @ts-ignore
Date.prototype.toLocaleString = function (a, b) {
if (b && Object.keys(b).length === 1 && "timeZone" in b && a === "en-US") {
return Intl.DateTimeFormat("en-us", {
year: "numeric",
month: "2-digit",
day: "2-digit",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false,
timeZone: b.timeZone,
})
.format(this)
.replace(/(\d{2})\/(\d{2})\/(\d{4}),/g, "$3-$1-$2");
}
// @ts-ignore
return this._toLocaleString(a, b);
};
I've ended up, at least temporarily, using this patch in my polyfill.android.ts
(plus all the formatjs stuff from before). It's absolutely not perfect... and really not a great solution, but seems to sort the issue for me.
Had run into same issue as @sohcah. Any update as official fix would be appreciated. Polyfills claim spec compliance https://formatjs.io/docs/polyfills/intl-datetimeformat/#default-timezone
I had to patch dayjs to isolate one call with ^^ workaround
Had run into same issue as @sohcah. Any update as official fix would be appreciated. Polyfills claim spec compliance https://formatjs.io/docs/polyfills/intl-datetimeformat/#default-timezone
I had to patch dayjs to isolate one call with ^^ workaround
Could you provide a code snippet ?
It's possible this might solve the problem on Android. In app build.gradle:
/**
* The preferred build flavor of JavaScriptCore.
*
* For example, to use the international variant, you can use:
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
*
* The international variant includes ICU i18n library and necessary data
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
* give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default.
*/
def jscFlavor = 'org.webkit:android-jsc-intl:+'
@troZee
create gist with patch we are using now https://gist.github.com/dlebedynskyi/81ff281b0db6aa69cdb360a1b609fb8a
this is essentially your suggested change, but as patch
+ // HACK https://github.com/iamkun/dayjs/issues/1377
+ i = Intl.DateTimeFormat('en-us', {
+ year: 'numeric',
+ month: '2-digit',
+ day: '2-digit',
+ hour: '2-digit',
+ minute: '2-digit',
+ second: '2-digit',
+ hour12: false,
+ timeZone: t,
+ })
@devinjameson
It's possible this might solve the problem on Android. In app build.gradle:
We are trying to avoid this exactly. There are bugs in current JSC used in RN related to DST (in Brazil for example). You also need to use https://formatjs.io/docs/polyfills for Hermes (RN 0.64) Last adding JSC increases your apk size by >3mb
@troZee
create gist with patch we are using now https://gist.github.com/dlebedynskyi/81ff281b0db6aa69cdb360a1b609fb8a
this is essentially your suggested change, but as patch
+ // HACK https://github.com/iamkun/dayjs/issues/1377 + i = Intl.DateTimeFormat('en-us', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false, + timeZone: t, + })
@devinjameson
It's possible this might solve the problem on Android. In app build.gradle:
We are trying to avoid this exactly. There are bugs in current JSC used in RN related to DST (in Brazil for example). You also need to use https://formatjs.io/docs/polyfills for Hermes (RN 0.64) Last adding JSC increases your apk size by >3mb
Patched it and now I'm getting this error.
ReferenceError: Property 'Intl' doesn't exist
Patched it and now I'm getting this error.
you are missing Intl library, probably on React Native Android - make sure you have JSC with Intl flavor, or latest Hermes with Intl, or add polyfill.
@dlebedynskyi Intl has been added in the latest version(0.8.1) of Hermes.
It's possible this might solve the problem on Android. In app build.gradle:
/** * The preferred build flavor of JavaScriptCore. * * For example, to use the international variant, you can use: * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` * * The international variant includes ICU i18n library and necessary data * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that * give correct results when using with locales other than en-US. Note that * this variant is about 6MiB larger per architecture than default. */ def jscFlavor = 'org.webkit:android-jsc-intl:+'
You are a life saver <3
@dlebedynskyi Intl has been added in the latest version(0.8.1) of Hermes.
But it doesn't come with timezone conversion support. When using Hermes on iOS, Intl is not available entirely.
The above patch causes midnight dates to be formatted as 2010-01-01 24:00:00
after applying .startOf("day")
(see here) once fed into the Date
constructor by the .tz
implementation (see here).
24:00
marks the end of the day, leading the Date
constructor to parse it as Jan 02 2010 00:00:00 GMT+0100
. By replacing hour12: false
with hourCycle: "h23"
in the patch above the correct date is formatted / converted when using .startOf("day")
in combination with timezones.
Before:
const originalDay = dayjs("2010-01-01T00:00:00Z").tz("UTC")
const startOfDay = originalDay.startOf("day")
console.log(startOfDay.valueOf() === originalDay.valueOf()) // false using above patch
After:
const originalDay = dayjs("2010-01-01T00:00:00Z").tz("UTC")
const startOfDay = originalDay.startOf("day")
console.log(startOfDay.valueOf() === originalDay.valueOf()) // true, using above patch with hourCycle: "h23" instead of hour12: false
The above patch causes midnight dates to be formatted as
2010-01-01 24:00:00
after applying.startOf("day")
(see here) once fed into theDate
constructor by the.tz
implementation (see here).
24:00
marks the end of the day, leading theDate
constructor to parse it asJan 02 2010 00:00:00 GMT+0100
. By replacinghour12: false
withhourCycle: "h23"
in the patch above the correct date is formatted / converted when using.startOf("day")
in combination with timezones.Before:
const originalDay = dayjs("2010-01-01T00:00:00Z").tz("UTC") const startOfDay = originalDay.startOf("day") console.log(startOfDay.valueOf() === originalDay.valueOf()) // false using above patch
After:
const originalDay = dayjs("2010-01-01T00:00:00Z").tz("UTC") const startOfDay = originalDay.startOf("day") console.log(startOfDay.valueOf() === originalDay.valueOf()) // true, using above patch with hourCycle: "h23" instead of hour12: false
Great find!
It's possible this might solve the problem on Android. In app build.gradle:
/** * The preferred build flavor of JavaScriptCore. * * For example, to use the international variant, you can use: * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` * * The international variant includes ICU i18n library and necessary data * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that * give correct results when using with locales other than en-US. Note that * this variant is about 6MiB larger per architecture than default. */ def jscFlavor = 'org.webkit:android-jsc-intl:+'
I have enable this line, but always getting error timeZone is not supported
@anggihnurh Im also still getting that error... did you figure it out?
There may still be some hair on this after reading through everyones comments, but here is a stitched together polyfill implementation to include in your top index.js file to get things working. Confirmed on iOS - I have not tried android yet.
Its important to note, the order you import the polyfills matters!!
Install things
yarn add @formatjs/intl-datetimeformat
yarn add @formatjs/intl-displaynames
yarn add @formatjs/intl-getcanonicallocales
yarn add @formatjs/intl-listformat
yarn add @formatjs/intl-locale
yarn add @formatjs/intl-numberformat
yarn add @formatjs/intl-pluralrules
yarn add @formatjs/intl-relativetimeformat
Add to your entry index.js file
import '@formatjs/intl-getcanonicallocales/polyfill';
import '@formatjs/intl-locale/polyfill';
import '@formatjs/intl-displaynames/polyfill';
import '@formatjs/intl-displaynames/locale-data/en'; // locale-data for en
import '@formatjs/intl-listformat/polyfill';
import '@formatjs/intl-listformat/locale-data/en'; // locale-data for en
import '@formatjs/intl-pluralrules/polyfill';
import '@formatjs/intl-pluralrules/locale-data/en'; // locale-data for en
import '@formatjs/intl-numberformat/polyfill';
import '@formatjs/intl-numberformat/locale-data/en'; // locale-data for en
import '@formatjs/intl-relativetimeformat/polyfill';
import '@formatjs/intl-relativetimeformat/locale-data/en'; // locale-data for en
import '@formatjs/intl-datetimeformat/polyfill';
import '@formatjs/intl-datetimeformat/locale-data/en'; // locale-data for en
import '@formatjs/intl-datetimeformat/add-all-tz'; // Add ALL tz data
// @ts-ignore
Date.prototype._toLocaleString = Date.prototype.toLocaleString;
Date.prototype.toLocaleString = function (a, b) {
if (b && Object.keys(b).length === 1 && 'timeZone' in b && a === 'en-US') {
return Intl.DateTimeFormat('en-us', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false,
timeZone: b.timeZone,
})
.format(this)
.replace(/(\d{2})\/(\d{2})\/(\d{4}),/g, '$3-$1-$2');
}
// @ts-ignore
return this._toLocaleString(a, b);
};
This is just a bump/summary of the folks above - they deserve the thank you if it works for you!
There may still be some hair on this after reading through everyones comments, but here is a stitched together polyfill implementation to include in your top index.js file to get things working. Confirmed on iOS - I have not tried android yet.
Its important to note, the order you import the polyfills matters!!
Install things
yarn add @formatjs/intl-datetimeformat yarn add @formatjs/intl-displaynames yarn add @formatjs/intl-getcanonicallocales yarn add @formatjs/intl-listformat yarn add @formatjs/intl-locale yarn add @formatjs/intl-numberformat yarn add @formatjs/intl-pluralrules yarn add @formatjs/intl-relativetimeformat
Add to your entry index.js file
import '@formatjs/intl-getcanonicallocales/polyfill'; import '@formatjs/intl-locale/polyfill'; import '@formatjs/intl-displaynames/polyfill'; import '@formatjs/intl-displaynames/locale-data/en'; // locale-data for en import '@formatjs/intl-listformat/polyfill'; import '@formatjs/intl-listformat/locale-data/en'; // locale-data for en import '@formatjs/intl-pluralrules/polyfill'; import '@formatjs/intl-pluralrules/locale-data/en'; // locale-data for en import '@formatjs/intl-numberformat/polyfill'; import '@formatjs/intl-numberformat/locale-data/en'; // locale-data for en import '@formatjs/intl-relativetimeformat/polyfill'; import '@formatjs/intl-relativetimeformat/locale-data/en'; // locale-data for en import '@formatjs/intl-datetimeformat/polyfill'; import '@formatjs/intl-datetimeformat/locale-data/en'; // locale-data for en import '@formatjs/intl-datetimeformat/add-all-tz'; // Add ALL tz data // @ts-ignore Date.prototype._toLocaleString = Date.prototype.toLocaleString; Date.prototype.toLocaleString = function (a, b) { if (b && Object.keys(b).length === 1 && 'timeZone' in b && a === 'en-US') { return Intl.DateTimeFormat('en-us', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, timeZone: b.timeZone, }) .format(this) .replace(/(\d{2})\/(\d{2})\/(\d{4}),/g, '$3-$1-$2'); } // @ts-ignore return this._toLocaleString(a, b); };
This is just a bump/summary of the folks above - they deserve the thank you if it works for you!
Unfortunately on android when I call dayjs.tz.guess()
return 'UTC' ...
Does anyone know what the problem is?
Heyo, I'm on RN 0.70.1 and facing issues with timezones on iOS (haven't tested Android yet).
I would like to get the timezone in relative GMT format, I've done the following:
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.extend(advancedFormat);
dayjs.tz.setDefault(RNLocalize.getTimeZone()); // Sets timezone to Europe/Berlin
dayjs().format('z') => prints 'GMT' expect 'GMT +2'
I've tried adding the polyfills and the code posted by @dorthwein but when I start the app I get the following error:
Have any of you got timezone support working on the latest RN versions?
There may still be some hair on this after reading through everyones comments, but here is a stitched together polyfill implementation to include in your top index.js file to get things working. Confirmed on iOS - I have not tried android yet. Its important to note, the order you import the polyfills matters!! Install things
yarn add @formatjs/intl-datetimeformat yarn add @formatjs/intl-displaynames yarn add @formatjs/intl-getcanonicallocales yarn add @formatjs/intl-listformat yarn add @formatjs/intl-locale yarn add @formatjs/intl-numberformat yarn add @formatjs/intl-pluralrules yarn add @formatjs/intl-relativetimeformat
Add to your entry index.js file
import '@formatjs/intl-getcanonicallocales/polyfill'; import '@formatjs/intl-locale/polyfill'; import '@formatjs/intl-displaynames/polyfill'; import '@formatjs/intl-displaynames/locale-data/en'; // locale-data for en import '@formatjs/intl-listformat/polyfill'; import '@formatjs/intl-listformat/locale-data/en'; // locale-data for en import '@formatjs/intl-pluralrules/polyfill'; import '@formatjs/intl-pluralrules/locale-data/en'; // locale-data for en import '@formatjs/intl-numberformat/polyfill'; import '@formatjs/intl-numberformat/locale-data/en'; // locale-data for en import '@formatjs/intl-relativetimeformat/polyfill'; import '@formatjs/intl-relativetimeformat/locale-data/en'; // locale-data for en import '@formatjs/intl-datetimeformat/polyfill'; import '@formatjs/intl-datetimeformat/locale-data/en'; // locale-data for en import '@formatjs/intl-datetimeformat/add-all-tz'; // Add ALL tz data // @ts-ignore Date.prototype._toLocaleString = Date.prototype.toLocaleString; Date.prototype.toLocaleString = function (a, b) { if (b && Object.keys(b).length === 1 && 'timeZone' in b && a === 'en-US') { return Intl.DateTimeFormat('en-us', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, timeZone: b.timeZone, }) .format(this) .replace(/(\d{2})\/(\d{2})\/(\d{4}),/g, '$3-$1-$2'); } // @ts-ignore return this._toLocaleString(a, b); };
This is just a bump/summary of the folks above - they deserve the thank you if it works for you!
Unfortunately on android when I call
dayjs.tz.guess()
return 'UTC' ...Does anyone know what the problem is?
.guess
is probably using some intl API that is not supported in hermes, you need to use the same that I'm using 'react-native-localize' and get the correct timezone from the native APIs
At least on RN 0.70 the Intl API has been implemented for Hermes on iOS. I don't know if there is some incompatibility with RN or not, but all the poly-fills and workarounds did not do anything for me. I gave up on trying to make .format('z')
work the way I wanted and resorted to using a third-party package to get the timezone abbreviation.
@dlebedynskyi Working with your patch thanks!
Any update on timezone plugin support for Hermes engine?
Just adding info here that dayjs.utc(timestamp).tz(timezone).format('myformat') does not work with hermes-enabled on iOS RN 0.71. it returns 'Invalid date'. Disabling hermes enginge fixes this problem for now.
Has there been any update on this? I am still seeing this issue on iOS
@troZee
create gist with patch we are using now https://gist.github.com/dlebedynskyi/81ff281b0db6aa69cdb360a1b609fb8a
this is essentially your suggested change, but as patch
+ // HACK https://github.com/iamkun/dayjs/issues/1377 + i = Intl.DateTimeFormat('en-us', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false, + timeZone: t, + })
@devinjameson
It's possible this might solve the problem on Android. In app build.gradle:
We are trying to avoid this exactly. There are bugs in current JSC used in RN related to DST (in Brazil for example). You also need to use https://formatjs.io/docs/polyfills for Hermes (RN 0.64) Last adding JSC increases your apk size by >3mb
thanks! you save my life 😭
Can confirm that this is still a problem.