moment / luxon

⏱ A library for working with dates and times in JS
https://moment.github.io/luxon
MIT License
15.05k stars 730 forks source link

setZone after fromISO to not change the provided iso #1605

Closed MuAladdinIbrahim closed 3 months ago

MuAladdinIbrahim commented 3 months ago

Is your feature request related to a problem? Please describe. When using Datetime.fromISO(x), x is 15:30 for example, it'll generate the date time object as expected but when using DateTime.fromISO(x).setZone(different time zone than server/mine) it will adjust the date time object based on that timezone.

The problem that, when I provide x, I provide it according to the timezone I used before to generate it so I want to set the same time zone without any changes in the result date time object. so I need the result to be the same hour as I provide and the timezone as I provide so instead of 2024-03-18T20:26:18.215+02:00 I want to keep it as 2024-03-18T20:26:18.215+03:00. (notice +03:00 and +02:00).

Describe the solution you'd like I believe adding a method to state the timezone of the provided iso will be great

diesieben07 commented 3 months ago

I am not 100% sure I am understanding your problem correctly. You can already tell Luxon the zone of to use if the ISO string does not contain an offset:

DateTime.fromISO('2024-03-18T20:26:18.215', { zone: 'UTC+3' })
MuAladdinIbrahim commented 3 months ago

I tried your solution but I'm facing that the Date I provided is considered as UTC and when I add zone: ''UTC+3", it changes both the DateTime itself and the offset. for example:

  expirationDate: DateTime { ts: 2024-03-26T03:27:23.846+03:00, zone: Asia/Riyadh, locale: en-US },
  now: DateTime { ts: 2024-03-26T00:43:14.915+03:00, zone: Asia/Riyadh, locale: en-US },
  date: 2024-03-26T00:26:23.846Z,
  expirationDateMillis: 1711412843846,
  nowMillis: 1711402994915 

and here's my function:

const isDateTimePassed = (date: Date, afterMins: number) => {
  const expirationDate = DateTime.fromJSDate(date, {
    zone: SATimezone,
  }).plus({
    minutes: afterMins,
  });
  const now = DateTime.now().setZone(SATimezone);
  console.log({
    expirationDate,
    now,
    date,
    expirationDateMillis: expirationDate.toMillis(),
    nowMillis: now.toMillis(),
    flag: now > expirationDate,
    flag2: now.toMillis() > expirationDate.toMillis(),
  });
  return now.toMillis() > expirationDate.toMillis();
};

afterMins in that case = 1

Also I tried convert it to isoString and use fromISO,

  const expirationDate = DateTime.fromISO(date.toISOString(), {
    zone: SATimezone,
  }).plus({
    minutes: afterMins,
  });  
diesieben07 commented 3 months ago

A Date (like a DateTime) points to a specific point in time, represented by a unix timestamp. Passing zone to fromJSDate won't change this point in time, it just changes which time zone you're viewing it from. An example:

const date = new Date('2024-01-01T10:00:00+00:00');
const dt1 = DateTime.fromJSDate(date, { zone: 'Europe/Berlin' });
const dt2 = DateTime.fromJSDate(date, { zone: 'America/New_York' });

// these two will show different offsets from UTC, but also different wall time, because they represent _the same instant in time_.
console.log(dt1.toISO(), dt2.toISO());

// you can verify this with toMillis:
console.log(dt1.toMillis(), dt2.toMillis());

In other words: No matter what you do, after calling fromJSDate the following will always be true:

const date = /* whatever */;
const dt = DateTime.fromJSDate(date);
dt.toMillis() === date.valueOf(); // Always true

I think your misconception is that Date represents day, month, year, hour, minute and seconds. It does not. It represents a unix timestamp.

I would highly suggest not working with Date at all. Use Luxon all the way.

icambron commented 3 months ago

If you really want to do this, use the keepLocalTime argument to setZone()