I need to make illusion of working in the selected by the user timezone. The problem that server and client code are stick to javascript date. So to achieve the requirement I have made mannualy mapping from utc to the date on the client side:
dateToServer(date) {
const momentDate = moment(date);
let serverDate = null;
if (momentDate.isValid() && date) {
const browserUtcOffset = momentDate.utcOffset();
serverDate = momentDate
.utc()
.subtract(this.clientTimeZoneOffset, 'minutes')
.add(browserUtcOffset, 'minutes')
.toDate();
}
return serverDate;
}
dateToClient(date) {
const momentDate = moment(date);
let uiDate = null;
if (momentDate.isValid() && date) {
const browserUtcOffset = momentDate.utcOffset();
uiDate = momentDate
.utc()
.subtract(browserUtcOffset, 'minutes')
.add(this.clientTimeZoneOffset, 'minutes')
.toDate();
}
return uiDate;
}
I'm adding/subtracting the browserUtcOffset, because it is adding/subtracting automatically by browser when the date is go between server and client.
It was working well, but this solution is missing handling of the DST. I'd like to check is DST active for the date and then add DST offset if need.
Here C# code, that can do this:
string timeZone = "Central Standard Time";
TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
DateTime date = new DateTime(2011, 3, 14);
Console.WriteLine(timeZoneInfo.BaseUtcOffset.TotalMinutes); // -360
Console.WriteLine(timeZoneInfo.GetUtcOffset(date).TotalMinutes); // -300
Console.WriteLine(timeZoneInfo.IsDaylightSavingTime(date)); // true
Console.WriteLine(timeZoneInfo.DaylightName);
Console.WriteLine(timeZoneInfo.SupportsDaylightSavingTime);
I have found the isDST in the momentjs, and when I have my windows local timezone to CST and check moment([2011, 2, 14]).isDST(); in browser console I see the true. How you can see the isDST is depends on the browser local time.
Next step try to use moment-timezone to do smth like I have done in C#. Unfortunately I don't understand how to achieve this. The first problem that as start point I have: UTC time, Base offset(-360 in C# sample), timezone name: Central Standard Time, but timezones in the moment-timezone are different.
moment.tz.names().map(name => moment.tz.zone(name)).filter(zone => zone.abbrs.find(abbr => abbr === 'CST') != null) this code returns 68 timezones.
Why there each of them have many abbrs? What mean untils? All I want to check if UTC time in the selected timezone, which is "Central Standard Time", the daylight saving day active. See C# sample one more time :)
string timeZone = "Central Standard Time";
TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
DateTime date = new DateTime(2011, 3, 14);
Console.WriteLine(timeZoneInfo.BaseUtcOffset.TotalMinutes); //-360
Console.WriteLine(timeZoneInfo.GetUtcOffset(date).TotalMinutes); //-300
Console.WriteLine(timeZoneInfo.IsDaylightSavingTime(date)); //true, I have checked is daylightsavingtime for the date
Console.WriteLine(timeZoneInfo.DaylightName);
Console.WriteLine(timeZoneInfo.SupportsDaylightSavingTime);
The application has been written on the angularjs 1.6.3.
A few things:
The Date object in JavaScript tracks a specific UTC-based point in time. You can see that timestamp with a call to .valueOf() or .getTime(). It is only certain functions and constructor parameters that work with the local time zone (such as .toString()). That local time zone is applied at the time the function is called. One cannot substitute a different time zone (except in the options object passed to toLocaleString). Thus, the Date object can not be converted from one time zone to another. Because your two functions both take in a Date object and return a Date object, all you are doing in the middle is picking a different point in time. This is also seen inside the function where you use the add and subtract methods of Moment. They manipulate the represented point in time - they do not change the time zone.
By passing this.clientTimeZoneOffset, You appear to have conflated a time zone with a time zone offset. These are separate concepts, as a time zone may go through multiple different offsets, due to DST but also due to changes in standard time, where they have occurred in history. See also "Time Zone != Offset" in the timezone tag wiki. It is not useful to pass just the client's offset, as that offset only applies to a single point in time. You cannot use it for time zone conversions, because it doesn't tell you anything about which offsets are used for other points in time.
Instead, pass a time zone identifier. On Windows in .NET, these look like "Central Standard Time" (representing both standard and daylight time despite the name), and in JavaScript and most other operating systems, IANA time zone names are used. They look like "America/Chicago". This is also covered in the timezone tag wiki.
If your .NET code is using Windows identifiers, you can use my TimeZoneConverter library to convert from Windows to IANA, then send that string to the browser as the time zone.
With Moment-Timezone, you can simply check the DST like so:
moment.tz([2011, 2, 14], 'America/Chicago').isDST()
Again, you should be using IANA time zone IDs, and TimeZoneConverter can provide them if you're using Windows time zones server-side.
You could consider using Noda Time on the server-side, with its TZDB time zone provider. That would allow you to use IANA time zones on both sides. This is also the case with TimeZoneInfo when run on .NET Core on Linux or Mac OSX.
The reason you see so many entries when you searched for abbreviations with CST is that time zone abbreviations are ambiguous. You may have meant US Central Standard Time, but you might also have meant Cuba Standard Time or China Standard Time, or a variety of other places that use that abbreviation.
Regarding the untils array, you generally don't need to concern yourself with that. It is part of the internal data that Moment-Timezone uses to pick the correct point in time for choosing a time zone offset and abbreviation.
You said at the end, something slightly different:
All I want to check if UTC time in the selected timezone, which is "Central Standard Time", the daylight saving day active.
The example I gave earlier assumed you were starting with that time zone's local time. If you're starting with the UTC time, then it's like this:
moment.utc([2011, 2, 14]).tz('America/Chicago').isDST()
Of course, you can pass various other supported inputs where I pass the array. The Moment docs can help you with available options.
Check please the answer of Matt Johnson for this question. It was very usefull for me.
Vocabulary
UI date - the date that user see on the html date input when
selecting the date. UI date is the native js date.
Server date - the UTC date that is stored in the
database.
timezone id - string, that identified the timezone. They are different for windows(.net), IANA(javascript) and rails conventions.
Problem
The main problem f.e. when a man logged in on the PC which is located in the timezone +3, but setting for his account is -6. The angularjs and kendo is using on the project and the kendo time is working only with native js date. And native js date is always in the browser timezone, for this example +3. But I should setup the stuff like it in -6. F.e. example user has selected time 07:00, the UTC will be 04:00 (07:00 - 3), but as for his account the timezone is -6, the utc should be 13:00 (07:00 + 6). And this conversations are applied automatically when we converting native js date(UI date) to UTC and backward. So decision to count the offset on the server and get rid off browser timezone offset: utcTime = UItime + browserOffset - clientTimezoneBaseOffset - daylightSavingTimeOffset.
However there is problem when need to get UI time back, f.e. today 06.06.2014 and the DST is true for this date, but when we are getting 03.03.2014 date from server we don't know if the DST was active for 03.03.2014.
Answer
In project on the server the .net, so in database the window's timezone IDs are stored. I have done it the next way: count on server DST for date ranges in the range from minimum date on the server and save on the client in the local storage. I like that in this case
the server is one source of truth, so the calculations can be performed on any client without manipulating the timezones. And no need to convert between IANA, Windows and rails timezones. But also there is a problem: need to precalculate DST in range from DateTime.MinValue to DateTime.MaxValue, currently to speed up I'm calculating DST for range from 01.01.2000 to DateTime.Now - it is enough when converting value from database to UI, because in database the min date is on the 2008 year, but is not enough for html input, because user can select value greater than DateTime.Now and lower then 01.01.2000. To fix it I'm, planning with use of TimeZoneConverter send to client the IANATimezoneId, and for cases when provided date(UI or server) is not in the range [01.01.2000, DateTime.Now] belong on the moment.utc(date).tz(IANATimezoneId).isDST().
This is new code on the server side
private class DaylightSavingTimeDescriptor
{
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public bool IsDaylightSavingTime { get; set; }
}
private string GetDaylightSavingTimeDescriptorsJson(string timeZone)
{
string daylightSaveingTimeDescriptorsJson = String.Empty;
if(timeZone != null)
{
List<DaylightSavingTimeDescriptor> dstList = new List<DaylightSavingTimeDescriptor>();
TimeZoneInfo timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZone);
DateTime startDate = new DateTime(2000, 1, 1);
DateTime dateIterator = startDate;
bool isDST = timeZoneInfo.IsDaylightSavingTime(startDate);
while (dateIterator < DateTime.Now)
{
bool currDST = timeZoneInfo.IsDaylightSavingTime(dateIterator);
if (isDST != currDST)
{
dstList.Add(new DaylightSavingTimeDescriptor()
{
EndTime = dateIterator.AddDays(-1),
IsDaylightSavingTime = isDST,
StartTime = startDate
});
startDate = dateIterator;
isDST = currDST;
}
dateIterator = dateIterator.AddDays(1);
}
daylightSaveingTimeDescriptorsJson = Newtonsoft.Json.JsonConvert.SerializeObject(dstList);
}
return daylightSaveingTimeDescriptorsJson;
}
And this is modified dateToServer, dateToClient on client side
export default class DateService{
constructor (authService) {
const authData = authService.getAuthData();
this.clientTimeZoneOffset = Number(authData.timeZoneOffset);
this.daylightSavingTimeRanges = authData.daylightSavingTimeRanges ? JSON.parse(authData.daylightSavingTimeRanges) : [];
}
getDaylightSavingTimeMinutesOffset(utcDate) {
const dstRange = this.daylightSavingTimeRanges.find(range => {
const momentStart = moment(range.startTime).utc();
const momentEnd = moment(range.endTime).utc();
const momentDate = moment(utcDate).utc();
return momentStart.isBefore(momentDate) && momentEnd.isAfter(momentDate);
});
const isDaylightSavingTime = dstRange ? dstRange.isDaylightSavingTime : false;
return isDaylightSavingTime ? '60' : 0;
}
dateToClient(date) {
const momentDate = moment(date);
let uiDate = null;
if (momentDate.isValid() && date) {
const browserUtcOffset = momentDate.utcOffset();
uiDate = momentDate
.utc()
.subtract(browserUtcOffset, 'minutes')
.add(this.clientTimeZoneOffset, 'minutes')
.add(this.getDaylightSavingTimeMinutesOffset(momentDate.utc()), 'minutes')
.toDate();
}
return uiDate;
}
dateToServer(date) {
const momentDate = moment(date);
let serverDate = null;
if (momentDate.isValid() && date) {
const browserUtcOffset = momentDate.utcOffset();
serverDate = momentDate
.utc()
.subtract(this.clientTimeZoneOffset, 'minutes')
.add(browserUtcOffset, 'minutes')
.subtract(this.getDaylightSavingTimeMinutesOffset(momentDate.utc()), 'minutes')
.toDate();
}
return serverDate;
}
}
PS
I want to get rid off offsets and use moment-timezone on the client and timezone converter on the server, but it will be later if customer ask, because I have never tried it before and I'm not sure that it will be working well, and current solution is working. Also the offsets anyway will be used for dateinputs components (angularjs), because they are using kendo-dateinput which ng-model is JS Date in browser local time, but I'm providing another timezone so need to transformations inside component.
PPS Best solution from my point of view, if timezone converter and moment-timezone will be working as expected
Anyway the whole app is operating the JS Date, so when user in Moscow with America in profile selects the datetime in kendo-input, or see setup kendo-scheduler, or display the date in the kendo-table, I need to manipulate with offsets. But instead of passing directly the client offset, I'm planning to pass IANA timezone id from the server with help of timezone converter and get the offset I need directly from moment-timezone.
Related
I have the UTC date string "2022-01-06T13:35:00Z" and the time zone string "Romance Standard Time". How can use that time zone string in JavaScript (in a browser) so that I can get the time corrected to 14:35?
The time zone libraries that I have found so far uses the IANA time zone, e.g. "Europe/Copenhagen", so a library that can convert "Romance Standard Time" to something like "Europe/Paris" would also answer my question. It's acceptable that this conversion is done in .NET and not JavaScript.
The key was to understand that "Romance Standard Time" is a Windows time zone ID. Everyone else uses IANA time zone IDs. They have the region/city format, e.g. "Europe/Copenhagen".
In .NET 6 it's possible to convert between the formats. Here's the code example from Date, Time, and Time Zone Enhancements in .NET 6.
// Conversion from Windows to IANA when a region is unknown.
string windowsTimeZoneId = "Eastern Standard Time";
if (!TimeZoneInfo.TryConvertWindowsIdToIanaId(
windowsTimeZoneId, out string ianaTimeZoneId))
{
throw new TimeZoneNotFoundException(
$"No IANA time zone found for "{windowsTimeZoneId}".");
}
Console.WriteLine($"{windowsTimeZoneId} => {ianaTimeZoneId}");
// "Eastern Standard Time => America/New_York"
// Conversion from Windows to IANA when a region is known.
string windowsTimeZoneId = "Eastern Standard Time";
string region = "CA"; // Canada
if (!TimeZoneInfo.TryConvertWindowsIdToIanaId(
windowsTimeZoneId, region, out string ianaTimeZoneId))
{
throw new TimeZoneNotFoundException(
$"No IANA time zone found for "{windowsTimeZoneId}" in "{region}".");
}
Console.WriteLine($"{windowsTimeZoneId} + {region} => {ianaTimeZoneId}");
// "Eastern Standard Time + CA => America/Toronto"
If you're on an earlier version of .NET you can use TimeZoneConverter. Here's a complete list of the time zones in case you need to build your own converter: windowsZones.json.
This answer to this questions suggests using NodaTime, so that is probably also a possibility: Convert Windows timezone to moment.js timezone?
JavaScript
If you want to do it with Javascript, there is multiple Libraries. I am most familiar with moment.js, however they are deprecated, which means you might want to use another library if you are working on a new project.
List of suggested libraries by moment.js.
Anyways, if you wish to work with moment and convert to timezones, you could easily do that using Moment-Timezone. All supported timezones.
For example:
const moment = require('moment-timezone');
const timeZone = "Europe/Paris";
const ISOString = "2022-01-06T13:35:00Z"; //2022-01-06T13:35:00Z
const momentDefault = moment(ISOString).format(); //2022-01-06T14:35:00+01:00
const momentTz = moment.utc(ISOString).tz(timeZone).format(); //2022-01-06T14:35:00+01:00
As you see, moment gets the timezone by default and handels the ISOString as a UTC value, so it automatically converts the value to the timezone of the client user.
You still can specifically tell moment to handle the ISOString as a UTC ISOString and then convert it to a specific Timezone you defined. In that way the client's timezone will be ignored.
EDIT:
Now I realized that the questioner wants to convert the timezone from Windows to Iana before converting it in Javascript, because most libraries including Moment.js don't support windows timezone ids.
I found a nice library here.
And here is an example how to convert the timezone first and then use moment.js.
import { findIana } from 'windows-iana';
const moment = require('moment-timezone');
const result = findIana('Romance Standard Time');
console.log(result); // ['Europe/Paris', 'Europe/Brussels', 'Europe/Copenhagen', 'Europe/Madrid', 'Africa/Ceuta']
const timeZone = result[0]; //doesn't really matter which one to take, if you are just converting the times.
const ISOString = "2022-01-06T13:35:00Z"; //2022-01-06T13:35:00Z
const momentDefault = moment(ISOString).format(); //2022-01-06T14:35:00+01:00
const momentTz = moment.utc(ISOString).tz(timeZone).format(); //2022-01-06T14:35:00+01:00
.NET C#
To convert a time to a specific timezone in C#, you dont need any extra packages, you can simply do that as following:
var isoString = "2022-01-06T13:35:00Z";
var utcDate= DateTime.Parse(isoString);
var timeZone = "Romance Standard Time";
var date = TimeZoneInfo.ConvertTime(utcDate, TimeZoneInfo.FindSystemTimeZoneById(timeZone)); //01/06/2022 14:35:00
Here you can find list of all supported timezoneIds
I'm working on a web application that supports link sharing. This link contains the clients selected dates (start and end date) from a date selector react component. As the user changes the selected dates they are accurately represented in the URL ready to be shared with other uses. When another user clicks on this link it should open the same web application except the default selected dates inside the date selector component will be parsed from the URL instead (if they exist otherwise uses the default).
This works exceptionally well when the links are shared between two people in the same time zone. However, if I send my link to someone in a different time-zone the selected dates for them are not the same as mine.
Currently when writing the local dates to the URL I am doing the following steps:
url.set("startDate", dates[0].toISOString());
url.set("endDate", dates[1].toISOString());
where date[0] and date[1] are date objects in the current users local time-zone.
When I parse the URL I do the following:
var startDate = new Date(url.get("startDate") ?? "");
var endDate = new Date(url.get("endDate") ?? "");
if (startDate.toString() === "Invalid Date") {
startDate = defaultStartDate; // user local time ( uses new Date() )
}
if (endDate.toString() === "Invalid Date") {
endDate = defaultEndDate; // user local time ( uses new Date() )
}
For example, I have two brokers running one in Eastern Standard and another in Pacific Standard. If I select 06/01/2021 and 06/05/2021 in Eastern Standard the link that is constructed looks like the following:
startDate=2021-06-01T04%3A00%3A00.000Z&endDate=2021-06-06T03%3A59%3A59.999Z
and when parsed by the user in Pacific Standard the resulting selected dates are:
05/31/2021 and 06/05/2021.
After some further debugging I believe this issue is occurring because the time set by default is 00:00:00 for start date and 23:59:59 for the end date. So when converting from EST -> PST new Date() is subtracting 4 hours from both dates (as it should). However, I need these to be the same dates across multiple time zones.
Given the strings are generated by toISOString, the resulting timestamp will be parsed to exactly the same time value by the built-in parser regardless of the user's system offset or settings.
The difference you see is from generating a local date and time from the time value based on different system settings. Given the same input timestamp, both systems should display exactly the same output timestamp if displayed for the same location, e.g. the following parses the timestamp in the OP, then presents the equivalent time in EDT and PDT respectively.
let s = '2021-06-01T04:00:00.000Z';
let startDate = new Date(s);
console.log(startDate.toLocaleString('en', {
timeZone: 'America/New_York',
timeZoneName: 'short'
}));
console.log(startDate.toLocaleString('en', {
timeZone: 'America/Los_Angeles',
timeZoneName: 'short'
}));
If you want the receiver to see the same date and time as the sender, then include the sender's IANA representative location (such as 'America/New_York' or encoded as 'America%2FNew_York') in the URL. But that doesn't change the generated time value, only the timestamp displayed by the various toString* methods.
I have a problem showing the same date in all timezones.
Users input is for example 01-01-2002 and I store it like a date with Eureope/Berlin timezone
parseFromTimeZone(String(birthDate), { timeZone: 'Europe/Berlin' })
and the result of parseFromTimeZone is this string '2001-12-31T23:00:00.000Z'. String date counts with timezone in Berlin that is why it is shifted for one hour.
And I need to get from '2001-12-31T23:00:00.000Z' this 01-01-2002 in all timezones.
I using formatISO(new Date(date), { representation: 'date' })) this returns 01-01-2002 when my timezone is Europe/Prague or Europe/Berlin
but when I change the timezone to America/Tijuana then formatISO returns 2001-12-31 and that is wrong I need to have the same date as is in Europe/Berlin always! Bud for Asia/Tokyo this function returns 01-01-2002 that is right ...
Some ideas? I have tried a lot of solutions but none works for all timezones...
I am using "date-fns": "^2.15.0", "date-fns-timezone": "^0.1.4"
Try this function with an ISO_8601 date, then change the timezone in your computer's settings and try again with the new timezone. It should print the same date on your web page for both time zones.
getDateFromISO(iso_string: string): string | Date {
if (!iso_string)
return null;
const isIsoDate = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/.test(iso_string); // check if string is in format 2022-01-01T00:00:00.000Z
const isDateTimeWithoutZone = /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(iso_string); // check if string is in format 2022-01-01T00:00:00
const isDateYMD = /\d{4}-\d{2}-\d{2}/.test(iso_string); // check if string is in format 2022-01-01
if (!isIsoDate && isDateTimeWithoutZone)
iso_string += '.000Z';
else if (!isIsoDate && isDateYMD)
iso_string += 'T00:00:00.000Z';
else if (isIsoDate)
iso_string = iso_string;
else
return iso_string;
const dateFromServer = new Date(iso_string);
const localOffset = new Date().getTimezoneOffset(); // in minutes
const localOffsetMillis = 60 * 1000 * localOffset;
const localDate = new Date(dateFromServer.getTime() + localOffsetMillis);
return localDate;
}
The Date object, despite its name, is does not represent a "date". It represents a timestamp. All that it stores internally is the number of milliseconds since the Unix epoch (which is UTC based). It outputs values based on either UTC or the local time zone of the machine where its running, depending on the function being called.
Thus, if you construct a Date object from a date-only value, you're really taking "the time at midnight" from that time zone and adjusting it to UTC. This is demonstrated by your example of 2002-01-01 in Europe/Berlin. Your treating that as 2002-01-01T00:00:00.000+01:00, which indeed has a UTC equivalent of 2001-12-31T23:00:00.000Z, and thus doesn't carry the same year, month, and day elements as the intended time zone.
You really only have two options to deal with date-only values if you want to prevent them from shifting:
Your first option is to use the Date object, but treat the input as UTC and only use the UTC-based functions. For example:
var dt = new Date(Date.UTC(2002, 0, 1)); // "2002-01-01T00:00:00.000Z"
var y = dt.getUTCFullYear(); // 2002
var m = dt.getUTCMonth() + 1; // 1
var d = dt.getUTCDate(); // 1
var dtString = d.toISOString().substring(0, 10) // "2002-01-01"
If you need to parse a date string, be aware that current ECMAScript spec treats date-only values as UTC (which is what you want), but in the past such behavior was undefined. Thus some browsers might create a local-time result from new Date('2002-01-01'). You may want to explicitly add the time and Z, as in new Date('2002-01-01' + 'T00:00:00.000Z') to be on the safe side.
If you intend to use date-fns, be careful - the parseISO and formatISO functions use local time, not UTC.
The second option is to not use the Date object. Keep the dates in their string form (in ISO 8601 yyyy-mm-dd format), or keep them in a different object of either your own construction or from a library.
The ECMAScript TC39 Temporal proposal is intended to fix such deficiencies in JavaScript. In particular, the Temporal.Date object (preliminary name) will be able to be used for date-only values without having the shifting problem you and so many others have encountered. The proposal is currently in Stage 2 of the ECMAScript process, so it's not available to use today, but this problem will be solved eventually!
I have a problem in my application. I display date in this format DD/MM/YYYY example 09/07/2020. But the problem is in my form if the user lives in reunion island enters the date 10/07/2020 (the date of today plus one day), the user lives in France sees 09/07/2020.
How can do in javascript to have the same date enters by the user who lives anywhere.
I want to have this : User lives in Reunion island enters 10/07/2020 and the user lives in France sees 10/07/2020
This is a recurrent issue in JS, and the solution is not easy as Date is stored as local time in JS.
You can use an ISO string to input your time: YYYY-MM-DDTHH:mm:ss.sssZ.
Executing this:
new Date('2020-01-01T00:00:00.000Z')
Will give the same results.
If you can, i'll advice you to use Moment.js, it will you save a lot of effort if you plan to do date operations.
If you want to save absolute dates i would recommend you to use a library like moment-timezone and save the dates in utc.
In order to do this you should do two things:
Set the date in utc before sending it to the server:
const parseDateBeforeSend = (selectedDate) => {
const parsedDate = moment(selectedDate).tz('utc', true).startOf('day').toDate()
return parsedDate
}
Transform the date to utc before showing it to the user:
const showDate = (date) => {
const parsedDate = moment(date).tz('utc').format("DD/MM/YYYY")
return parsedDate
}
For your information:
When using tz function in step 1 you can see that it has a second parameter set to true, what this does is to keep the original time and only update the timezone. This would keep the original date you want to show.
In the second step we omit this parameter since we want to show the actual date saved in utc.
I've got a form where I input an event that starts at a certain time. Let's say 9am.
To assign a date/time object I'm using MomentJs. The issue comes when displaying it in different time-zones.
In London will show up 9am as intended - in Kiev will show 11am.
How can I make MomentJS and the browser ignore which timezone is relevant for the user, and just displaying the time I'm giving?
Here's my code:
<p>
Start time:
{moment(event.startDate).format("HH:mm")}
</p>
Assuming you have stored the date as utc (which in this case you probably should have), you could use the following:
moment.utc(event.startDate).format("HH:mm")
Let me provide an alternative answer in Vanilla JavaScript. If you want to make it timezone 'neutral', you can first convert it to UTC using toISOString().
const current = new Date();
const utcCurrent = current.toISOString();
console.log(utcCurrent);
If you want to convert it to a specific timezone, such as London, you can use toLocaleString(). Do take note of the browser support for the timezone though.
const londonTime = new Date().toLocaleString('en-US', { timeZone: 'Europe/London' })
console.log(londonTime);
What you want is a normalized Datetime. This can get a little confusing since the concept of timezones is a rather arbitrary construct.
I like to think of Datetime values as "absolute" and "relative". An "absolute" Datetime is one that is true regardless of which timezone you're in. The most common example of these are UTC(+000) and UNIX Time (also known as Unix epoch, POSIX Time or Unix Timestampe).
UTC is pretty obvious. Its the current time at +000 timezone. UNIX time is a bit more interesting. It represents the number of seconds that have elapsed since January 1, 1970.
You should always store data, in both client and backend, as an "absolute" time. My preference is UNIX time since its represented as a single integer (nice and clean).
moment.js does this for you. When you instantiate your moment object, you can use:
var date = moment.utc(utcString)
or for Unix Time
var date = moment.unix(unixInt)
You can then use this object to display the date in any form you wish:
console.log(date.tz.("America/Toronto"))
The only way I could solve this is by removing the timezone and milliseconds info from the string. I used date-fns lib but I imagine moment will work the same way.
import { format } from 'date-fns'
const myDateTimeString = '2022-02-22T19:55:00.000+01:00'
const dateTimeWithoutTimezone = myDateTimeString.slice(0, 16) // <- 2022-02-22T19:55
format(new Date(dateTimeWithoutTimezone), 'HH:mm')