mosestef
Contributor
Contributor

[solved] How to convert a Date from one Timezone to another Timezone? (moment.js library integration)

Jump to solution

Hi VM Community!

I need to convert a date from GMT into a target timezone, for example 'Europe/Berlin' or 'Asia/Singapore'.

Currently my limitation is that the Date Class in VM doesn't support any automatic timezone conversions.

For manual conversions in the Javascript code my main issue is that I cannot handle daylight savings time switches. Any JS library that you would usually use to handle this is unavailable.

Any good tips on how to implement this?

My full requirement is to take a GMT Date and print it out in 4 strings for easy worldwide readability:

GMT

CET / CEST

SGT

PDT / PST

Thanks,

Steve

------

UPDATE: Solved by integrating the moment.js library.

Can be found as attachment.

0 Kudos
1 Solution

Accepted Solutions
eoinbyrne
Expert
Expert

Had another crack at this and got it working

Here's the workflow code & output

var moment = System.getModule("com.triangle.util").getMomentInstance();

var momentTZ = System.getModule("com.triangle.util").getMomentTZ(moment);

System.log(moment.locale());

moment.tz.add([

    'America/Los_Angeles|PST PDT|80 70|0101|1Lzm0 1zb0 Op0',

    'America/New_York|EST EDT|50 40|0101|1Lz50 1zb0 Op0',

'Europe/London|GMT BST BDST|0 -10 -20|0101010101010101010101010101010101010101010101010121212121210101210101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2axa0 Rc0 1fA0 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 2Rz0 Dc0 1zc0 Oo0 1zc0 Rc0 1wo0 17c0 1iM0 FA0 xB0 1fA0 1a00 14o0 bb0 LA0 xB0 Rc0 1wo0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1a00 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1tA0 IM0 90o0 U00 1tA0 U00 1tA0 U00 1tA0 U00 1tA0 WM0 1qM0 WM0 1qM0 WM0 1tA0 U00 1tA0 U00 1tA0 11z0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 14o0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00|10e6',

]);

var newYork    = moment.tz("2014-06-01 12:00", "America/New_York");

var losAngeles = newYork.clone().tz("America/Los_Angeles");

var london     = newYork.clone().tz("Europe/London");

System.log(newYork.format());    // 2014-06-01T12:00:00-04:00

System.log(losAngeles.format()); // 2014-06-01T09:00:00-07:00

System.log(london.format());     // 2014-06-01T17:00:00+01:00

Output in the log trace looks like this

pastedImage_2.png

I had to create two actions to house the contents of moment.js and momenttz.js. There is a dependency from TZ so you need both

The action code contains pretty much the exact content of the JS files but with two subtle changes (may not be the absolute best way to do this - perhaps some JS expert can chime in?)

Changed the beginning of the module content to just leverage it as a straight 'Constructor' fuction

pastedImage_3.png

pastedImage_4.png

At the bottom of the action just return the result calling the constructor function. The TZ action requires a moment instance as a parameter (type is Any). I did the same things to the action for the base momentjs creation

The logging shows the functions look to work OK but as a general way of doing this I'm not sure about the sanity of instantiating the entire object set each and every time you use it. I'm running a single workflow here but on a busy server you might get OutOfMemory errors in the server JVM if lots of workflows use this at the same time

Anyway, as an interesting exercise it was fun Smiley Happy

View solution in original post

5 Replies
eoinbyrne
Expert
Expert

All of your TZ offsets are known / fixed + the Date class has a method to convert the current date into a UTC date instance (basically removes the TZ offset of the vRO server as defined in the Linux & Java Locale settings)

So you should be able to use use that info to do the following

1. Convert your input date to it's UTC equivalent

2. For each known offset

3. Create a new date instance and add the offset, then print the result

Alternatively, if you can find the function code from a library that has this operation built in, then you can create an action and copy the code into it in vRO.

-HTH

0 Kudos
mosestef
Contributor
Contributor

Hi!

The timezone offsets are not fixed but change during Daylight Savings Time. I would need to get the info that DST is active from the VRO somehow but found nothing that helped.

Regarding the library:

One example library that can handle this would be the moment library  https://momentjs.com/timezone/

I just dont understand how to use it in VRO.

The full library code would be available at :

https://momentjs.com/downloads/moment-timezone-with-data.js

But how would i integrate it?

Thanks!

0 Kudos
eoinbyrne
Expert
Expert

moment and moment-tz are delivered as modules meant for inclusion in Websites and Node.js servers. I've had a quick look but my JS-foo is not up to sorting out how to use these with vRO (I'm a Java dev so the ins and outs of JS module loading is not my area sadly). I tried creating an action with the entire content of the moment.js module but can't see how to instantiate an instance of a Moment

i.e., doing this

new Moment();

just throws an error in vRO.

I had a read down through moment-tz and noticed that it has a large static lookup table buried in the middle which references all the TZ data. Since this is all the library you want to use does, I would still say that since you only have 4 timezones in your requirement the option is still there to just store static data for the 4 zones you're interested in (e.g., Name, offset from GMT, usesDST, DSTBoundary times (since these are static for the TZ relative to the current day in any given year))

If you go that far it's just Date maths which the JS implementation in vRO can do easily

Best of luck with it all

-Eoin

0 Kudos
eoinbyrne
Expert
Expert

Had another crack at this and got it working

Here's the workflow code & output

var moment = System.getModule("com.triangle.util").getMomentInstance();

var momentTZ = System.getModule("com.triangle.util").getMomentTZ(moment);

System.log(moment.locale());

moment.tz.add([

    'America/Los_Angeles|PST PDT|80 70|0101|1Lzm0 1zb0 Op0',

    'America/New_York|EST EDT|50 40|0101|1Lz50 1zb0 Op0',

'Europe/London|GMT BST BDST|0 -10 -20|0101010101010101010101010101010101010101010101010121212121210101210101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010|-2axa0 Rc0 1fA0 14M0 1fc0 1g00 1co0 1dc0 1co0 1oo0 1400 1dc0 19A0 1io0 1io0 WM0 1o00 14o0 1o00 17c0 1io0 17c0 1fA0 1a00 1lc0 17c0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1cM0 1io0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1io0 1qM0 Dc0 2Rz0 Dc0 1zc0 Oo0 1zc0 Rc0 1wo0 17c0 1iM0 FA0 xB0 1fA0 1a00 14o0 bb0 LA0 xB0 Rc0 1wo0 11A0 1o00 17c0 1fA0 1a00 1fA0 1cM0 1fA0 1a00 17c0 1fA0 1a00 1io0 17c0 1lc0 17c0 1fA0 1a00 1io0 17c0 1io0 17c0 1fA0 1a00 1a00 1qM0 WM0 1qM0 11A0 1o00 WM0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1tA0 IM0 90o0 U00 1tA0 U00 1tA0 U00 1tA0 U00 1tA0 WM0 1qM0 WM0 1qM0 WM0 1tA0 U00 1tA0 U00 1tA0 11z0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1o00 14o0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00 11A0 1qM0 WM0 1qM0 WM0 1qM0 WM0 1qM0 11A0 1o00 11A0 1o00|10e6',

]);

var newYork    = moment.tz("2014-06-01 12:00", "America/New_York");

var losAngeles = newYork.clone().tz("America/Los_Angeles");

var london     = newYork.clone().tz("Europe/London");

System.log(newYork.format());    // 2014-06-01T12:00:00-04:00

System.log(losAngeles.format()); // 2014-06-01T09:00:00-07:00

System.log(london.format());     // 2014-06-01T17:00:00+01:00

Output in the log trace looks like this

pastedImage_2.png

I had to create two actions to house the contents of moment.js and momenttz.js. There is a dependency from TZ so you need both

The action code contains pretty much the exact content of the JS files but with two subtle changes (may not be the absolute best way to do this - perhaps some JS expert can chime in?)

Changed the beginning of the module content to just leverage it as a straight 'Constructor' fuction

pastedImage_3.png

pastedImage_4.png

At the bottom of the action just return the result calling the constructor function. The TZ action requires a moment instance as a parameter (type is Any). I did the same things to the action for the base momentjs creation

The logging shows the functions look to work OK but as a general way of doing this I'm not sure about the sanity of instantiating the entire object set each and every time you use it. I'm running a single workflow here but on a busy server you might get OutOfMemory errors in the server JVM if lots of workflows use this at the same time

Anyway, as an interesting exercise it was fun Smiley Happy

View solution in original post

mosestef
Contributor
Contributor

Thanks for your very helpful guidance eoinbyrne!

I integrated the libraries now and was able to fulfill my usecase easily with that.

I have created a documented package that includes:

1 Action that encapsulates the moment.js library

1 Action that encapsulates the moment-timezones library

1 Action to directly get moment.js + activated TZ handling (to reduce overhead in usage)

If anyone else needs this moment.js package implementation i will update my initial question and attach it as complete package there!

Again, thanks alot.

0 Kudos