quasarframework / quasar

Quasar Framework - Build high-performance VueJS user interfaces in record time
https://quasar.dev
MIT License
25.95k stars 3.52k forks source link

Qdate/Qtime UTC mode #5884

Closed pw4rn3r closed 4 years ago

pw4rn3r commented 4 years ago

Is your feature request related to a problem? Please describe. There is no way to directly work with UTC time with Qdate. The date picker syncs to local time.

Describe the solution you'd like Ideally, there would be a utc prop or something similar that would turn on UTC mode. Upon opening the date picker, it should show the current date based on Zulu/UTC time instead of local time. Picking a date would output UTC time.

This should work in concert with Qtime and Qinput so that the date or time popup proxies would both provide UTC time. Ideally, the Qinput/Qdate/Qtime combo could easily support ISO format (e.g., ' 2020-03-09T06:00:00.123Z'

Describe alternatives you've considered I could use computed props to translate both directions so that a Qinput can show UTC time, but the pickers are still misleading. Now and today buttons also would not be correct unless I override those.

Additional context N/A

adamlytics commented 4 years ago

I imagine these all utilize the date utils. I've found that using the date utils is problematic when you are working with the first of the month. I can pass in the first of the month, and when I go to format it, it gives me the last day of the prior month due to converting my local timezone to UTC in the process. It would be great if it ignored the timezone and used exactly what it has.

pdanpdan commented 4 years ago

It depends on how you format the date:

(new Date(2019-01-01)).toISOString() => 2019-01-01T00:00:00.000Z
(new Date(2019/01/01)).toISOString() => 2018-12-31T22:00:00.000Z

But an expected convention is that the client deals with wall clock dates.

rstoenescu commented 4 years ago

QDate and QTime operate with Strings, so it is up to the developer to handle whatever timezone he/she wants...

carueda commented 4 years ago

(first off, @rstoenescu , I'm so glad you have recovered!)

I think I would vote to re-open this issue at least regarding:

Now and today buttons also would not be correct unless I override those

In particular, the now-btn option in QTime always uses local time, so this won't help in the case where the interaction with the user is in the context of UTC (or some other timezone). So, if some more general UTC is not acceptable as proposed in this ticket in general, what about allowing now-btn to also be a function besides Boolean? In the function case, one can then take care of providing whatever makes sense in terms of appropriate timezone. This is basically just a quick reaction now that I'm dealing with this in a particular application. Other suggestions welcome of course. Thanks!

Tofandel commented 1 year ago

I don't think dealing with dates in UTC on a frontend is a good solution, it's much better to deal with time strings in an ISO format with your users's timezone info, then if you want to convert it to UTC you can easilly parse this valid iso 8601 date in your backend and convert it to UTC there

This is my component to handle this

<template>
  <QInput
    :model-value="format(modelValue, {time: true})"
    readonly
  >
    <template #prepend>
      <QIcon :name="farCalendar" />
    </template>
    <QPopupProxy
      class="flex"
      @show="preventClosing"
      @hide="unpreventClosing"
    >
      <QDate
        :model-value="modelValue"
        mask="YYYY-MM-DDTHH:mm:ssZ"
        today-btn
        color="primary"
        @update:model-value="emitVal"
      />
      <QTime
        :model-value="modelValue"
        format24h
        mask="YYYY-MM-DDTHH:mm:ssZ"
        color="primary"
        @update:model-value="emitVal"
      />
    </QPopupProxy>
  </QInput>
</template>

<script setup>
import { farCalendar } from '@quasar/extras/fontawesome-v6';

const format = (date, opts, locale = 'fr-FR') => {
  if (!date) {
    return '';
  }
  opts = Object.assign({ date: true, time: false }, opts);
  opts = Object.assign({ weekday: false, month: opts.date, day: opts.date, year: opts.date, second: false, hour: opts.time, minute: opts.time }, opts);
  return new Date(date)[opts.time ? 'toLocaleTimeString' : 'toLocaleDateString'](locale, {
    weekday: opts.weekday ? (typeof opts.weekday === 'boolean' ? 'long' : opts.weekday) : undefined,
    year: opts.year ? 'numeric' : undefined,
    month: opts.month ? (typeof opts.month === 'boolean' ? 'long' : opts.month) : undefined,
    day: opts.day ? 'numeric' : undefined,
    hour: opts.hour ? 'numeric' : undefined,
    minute: opts.minute ? 'numeric' : undefined,
    second: opts.second ? 'numeric' : undefined,
  });
};

const props = defineProps({
  modelValue: String,
});

const emit = defineEmits(['update:modelValue']);

const emitVal = (val) => {
  const currentOffset = new Date(props.modelValue).getTimezoneOffset();
  const newOffset = new Date(val).getTimezoneOffset();
  if (currentOffset !== newOffset) {
    const offset = (newOffset <= 0 ? '+' : '-') + Math.floor(Math.abs(newOffset) / 60).toString().padStart(2, '0') + ':' + (newOffset % 60).toString().padStart(2, '0');
    emit('update:modelValue', val.replace(/([+-]\d{2}:?\d{2}|Z)$/, offset));
  } else {
    emit('update:modelValue', val);
  }
};
</script>

Converting to UTC will not cause any problem, but dropping the timezone part without converting because you're assuming all your dates are received in UTC (like laravel does and shame on them for doing so) will cause problems because your user will keep getting a date that is being offset by their timezone everytime they save