1

I have a JavaScript function that takes a number (1-31), creates a Date and sends it to a server via an AJAX request:

sendDate(d) {
    let date = new Date(this.year, this.month, d);
    htmx.ajax("GET", "/some/url", { values: { "date": date.toISOString() } });
}

The problem is, that JavaScript is doing some timezone correction which has the effect, that if I create a date like new Date(2022, 09, 14) I get a date Wed Sep 14 2022 00:00:00 GMT+0200 (Central European Summer Time) which when converted to ISO format becomes 2022-09-13T22:00:00.000Z, ie. the previous day.

I know I can use .toLocaleDateString(), but I would like to stick to the ISO-format and I would also like to avoid hacky solutions such as always creating the date with some specified time at the middle of the day or whatever.

Is there a simple way to create a regular date object and pass it on to a server without timezone shenanigans?

andreashhp
  • 485
  • 2
  • 16

3 Answers3

3

Values passed to the Date constructor are treated as local, toISOString uses UTC. Unfortunately ECMAScript doesn't have a timezone–free, date–only form.

If you want to use toISOString to format the timestamp, then one solution is to parse the values as UTC initially, e.g.

sendDate(d) {
    let date = new Date(Date.UTC(this.year, this.month, d));
    htmx.ajax("GET", "/some/url", { values: { "date": date.toISOString() } });
}

Example:

let d = new Date()
let year = d.getFullYear();
let month = d.getMonth()
let day = d.getDate();
let date = new Date(Date.UTC(year, month, day))
// Always shows the current local date as 00:00:00Z
console.log( date.toISOString() );

You should also trim the Z from the end since the timestamp really is local.

RobG
  • 142,382
  • 31
  • 172
  • 209
2

There’s a way to do that: store the date as the UNIX timestamp. I do it this way and when it’s needed, I pass the timestamp from server to client and convert it into local date.

That said, I don’t think it’s a good idea to build the date on client and pass it to the server, since it could be modified on client. I believe it’s mostly done this way (and that’s also how it’s done in my projects): on the server side, you check the current timestamp (at the time request is sent) and do with it whatever you want.


It appears that you allow your user to pick a date. It would be a good approach to convert it then to timestamp and pass it to the server.

How would you do that? If I return date.getTime() and then parse it (in python) with datetime.fromtimestamp(int(request.GET['date']) / 1000) I still get the same timezone issue...

You won't have any issues in this case - apart from the fact your server just could happen to have a different local time. A time-zone independent solution would have to use utcfromtimestamp instead of fromtimestamp.

Either way, the timestamp itself can't have any timezone problems by definition.

nicael
  • 18,550
  • 13
  • 57
  • 90
  • Right. It must be determined based on application semantics whether a date like that is supposed to mean something relative to the *client*, or something relative to the *server*. – Pointy Apr 06 '22 at 16:48
  • How would you do that? If I return `date.getTime()` and then parse it (in python) with `datetime.fromtimestamp(int(request.GET['date']) / 1000)` I still get the same timezone issue... – andreashhp Apr 06 '22 at 17:05
  • Basically, all I want on the client side is for the client to be able to pick a date and let the server know about it. – andreashhp Apr 06 '22 at 17:06
  • @andreashhp yes but is the date something meaningful for work that the server will do? Or is it something meaningful to the person using the client? If you tell Google to wake you up at 7:00 in the morning, you expect Google to know that you mean 7:00 where you are. If you tell a server "Start a backup at 23:00", you probably mean 23:00 *at the server*. – Pointy Apr 06 '22 at 17:10
  • @andreashhp do you mean that client picks the date? if you do, which date to pass, in local time, in server time or maybe GMT? – nicael Apr 06 '22 at 17:14
  • @Pointy It's part of a booking application, where the client have to pick a date (there is a js based date picker component to interact with), and the server will then respond with a list of available time slots for the chosen date. – andreashhp Apr 06 '22 at 17:17
  • @nicael I think the problem is on the client side in the fact that, as soon as I do any operation on the date, that I have created via `date = new Date(2022, ...)`, eg. such as converting it to a UNIX timestamp (via `date.getTime()`), the timezone is factored in and two hours (in this specific case) is subtracted. So the UNIX timestamp that I'd be sending to the server points to 2022-09-13T22:00:00 rather that 2022-09-14T00:00:00. – andreashhp Apr 06 '22 at 17:37
  • @andreashhp timezone must be factored, otherwise you pass a wrong date. If the date on the client is different from UTC, then it would be converted to UTC. – nicael Apr 06 '22 at 17:39
  • @andreashhp it appears that you're missing the fact that timezone isn't something which is present in UTC time or the time formatted as timestamp. For example, if my time is 14:00 UTC+2, it's the same as 12:00 UTC. If you choose to save a timestamp which also points to 14:00 in this case, then you're saving a wrong date, because 14:00 UTC is not 14:00 UTC+2 – nicael Apr 06 '22 at 17:40
  • @nicael I see and I do understand that. I guess my issue stems from the fact that I basically just want to pick a date, not a datetime. But I think I see your point. I will probably default to then only pass date information, specified manually in a format like `2022-09-14T` or something. – andreashhp Apr 06 '22 at 17:49
  • 2
    Using a "UNIX timestamp" doesn't fix the OP's issue, which is parsing the year, month and day as local then generating a UTC timestamp. The real issue is that ECMAScript doesn't have a date–only form, the closest is YYYY-MM-DD but the TC39 in its wisdom decided to make it UTC, which is inconsistent with ISO 8601. – RobG Apr 07 '22 at 01:12
1

See RobG's Answer... using the new Date(Date.UTC(year, month, day)) formatting is much better.

BeRT2me
  • 12,699
  • 2
  • 13
  • 31
  • The first suggestion (which requires me to divide `.getTimezoneOffset()` with 1000) still gives me the same issues... The second suggestion could work, but I do also need the date elsewhere. I could of course do both, but I mean... I can't believe this can't be done properly. – andreashhp Apr 06 '22 at 17:12
  • Did you add the `0,` and make sure the `.getTimezoneOffset` was negative `-` as well? There shouldn't have been a need to divide by 1000... – BeRT2me Apr 06 '22 at 17:34
  • Ah, I missed the `0,`! That actually works. I'll give some time, and if I don't find a better solution, I will accept this! :) – andreashhp Apr 06 '22 at 17:41
  • Added an example of a method that builds the expected string first~ – BeRT2me Apr 06 '22 at 17:59
  • No. Because the date represented by the parsed year, month and day may represent a time when the offset is different to the current local offset, e.g. either side of a DST or historic offset change. – RobG Apr 07 '22 at 01:14
  • That is a good point, Timezone offsets are unfortunately not static. What about the ISOString creation method? – BeRT2me Apr 07 '22 at 03:20