Reputation: 1391
This question seems similar to Deprecation warning: moment construction falls back to js Date, as the same warning appears. However, that warning is not the focus of this question, and the solution described is not well suited for my use case. I've edited the appropriate passages below to explain why this is so.
Consider a web application where the user can select a time range of images made by a stationary camera. Obviously, this camera may not be in his own timezone. Add to that that the REST-API delivering the images does everything in UTC.
I am using angular4, moment.js and moment timezone.
So he can choose whether he wants to see the images with timestamps in his local timezone, the timezone the camera is in, or in UTC. So far so well, everything is working up to this point.
Where I get in trouble is when setting the time range. Obviously, the start, and end times the user sets should be interpreted as being in the timezone he has selected for display, so I can convert them to UTC before sending the request to the server. The Date object I get from the date picker (primeng calendar in this case, but that doesn't really matter) is of course in browser local time.
So what I need to be able to do is take that Date and interpret it in the timezone of the camera, as is, without converting it. So if the user sets "from 10:00 to 17:00" while sitting in timezone UTC -2, and the camera is located in UTC + 2, I need to get "10:00 to 17:00 in UTC +2", NOT "14:00 to 21:00 in UTC +2".
I tried several approaches, some of which even work, but each seems to have their own caveats, and they're all butt-ugly because they do unnecessary string conversions. For example:
moment.tz(date.toLocaleString, cameraTimezone)
This works, but throws a deprecation warning:
value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js
Date()
, which is not reliable across all browsers and versions.
I could get rid of that warning by specifying what format the passed date string is in, but I'm facing the problem that neither toLocaleString() nor toString() are consistent. toLocaleString() will obviously give me another format depending on the locale, and toString() yields different results in different browsers, so how would I know what I get?
So I try with a proper format, but if I use Date::toIsoString
directly, the moment I get has been converted to UTC, which is not what I need either:
let datestring = date.toISOString(); //datestring will be in utc
const mom = moment.tz(datestring, cameraTimezone); //moment will therefore be the wrong time.
There's the obvious way of creating a string of the Date object myself (which is really ugly in javascript) and then parsing it into the moment by specifying that format. But all of these string conversions feel more like a hack than a proper solution, and I'd really like to get rid of them altogether and just tell moment "Here you have a date, it's supposed to be in this timezone, even if it says it isn't". Is there a way to do that?
Upvotes: 1
Views: 288
Reputation: 147523
It may suit to construct an ISO 8601 compliant string based on the local date and user–selected time and timezone, e.g.
window.onload = function() {
var form = document.getElementById('cameraTimeDetail');
form.addEventListener('submit', getPicISO, false);
}
function getPicISO(e) {
// For demo
e.preventDefault();
function z(n){return ('0'+n).slice(-2)}
var date = new Date();
var form = this;
// Use local date parts
var s = date.getFullYear() + '-' +
z(date.getMonth()+1) + '-' +
z(date.getDate()) + 'T' +
// Use input timezone and time
form.pictureTime.value +
form.cameraTimezone.value;
// Consructed ISO string
form.timeString.value = (s);
// Convert to UTC ISO String
form.timeStringUTC.value = new Date(s).toISOString();
return false;
}
input, button {
width: 15em;
}
<form id="cameraTimeDetail">
<table>
<tr>
<td>Enter timezone (e.g. -08:00, +05:30)
<td><input name="cameraTimezone" value="+05:30">
<tr>
<td>Enter time (HH:mm)
<td><input name="pictureTime" value="14:37">
<tr>
<td><input type="reset">
<td><button>Get pic</button>
<tr>
<td>Selected date and time:
<td><input name="timeString" readonly>
<tr>
<td>Equivalent UTC:
<td><input name="timeStringUTC" readonly>
</table>
</form>
Obviously that is simplified and needs validation of input values but shows the logic. You may also want to let the user select the date, which can use the same logic.
Upvotes: 1