0

In my Laravel application I have a form in which you put the date to capture the date of an expense. This field is a MySQL DATE field so the format is yyyy-mm-dd.

The timezone in my application is set within config/app.php like so.

  /*
    |--------------------------------------------------------------------------
    | Application Timezone
    |--------------------------------------------------------------------------
    |
    | Here you may specify the default timezone for your application, which
    | will be used by the PHP date and date-time functions. We have gone
    | ahead and set this to a sensible default for you out of the box.
    |
    */

    'timezone' => 'Europe/London',

Fairly recently in Britain we entered into British Summer Time (as we do every year) and this raises my actual question.

A user used a date picker to select 2022-04-04 and this was entered into the database as 2022-04-04.

The issue is with the return value from an accessor.

/**
 * Force date format.
 *
 * @param  [type] $value
 * @return void
 */
public function getDateAttribute($value)
{
    return Carbon::parse($value)->format('d M Y');
}

(Note that the date field has also been cast to a Carbon instance.)

This comes back as 03 Apr 2022, and if you add Y-m-d H:i:s it comes back as 2022-04-03 11:00

enter image description here

Given the picture above can I assume that Europe/London is equivalent to GMT?

Update

I failed to mention that I'm using vue-datepicker so when I select a date it looks like this:

enter image description here

I then pass this through moment.js like so.

"date": moment(this.date),

Which sends through as follows

enter image description here

Update 2

I send the date to my backend via an axios request and I format it with moment.js like so:

axios.post('/api/expenses/personal', {
    "date": moment(new Date(this.date)).format('YYYY-MM-DD'),
    "business_reason": this.business_reason,
    "total_before_vat": this.total_before_vat,
    "category": this.category,
    "centre_cost_centre_id": this.centre_cost_centre_id,
    "is_foreign_expense": this.is_foreign_expense,
    "total_before_conversion": this.total_before_conversion,
    "conversion_rate": this.conversion_rate,
    "currency": this.currency,
    "has_vat": this.has_vat,
    "vat_claimed": this.vat_claimed,
    "total": this.calculatedTotal,
    "expense_group_id": this.$route.params.expense_group_id
}) 
Jesse Luke Orange
  • 1,949
  • 3
  • 29
  • 71
  • Not sure what exactly you mean by assuming them to be "equivalent." `Europe/London` does have DST, whereas `GMT` does not. – CBroe Apr 20 '22 at 14:14
  • Apologies, I saw in this answer https://stackoverflow.com/questions/30477921/php-timezone-issue-bst-and-gmt that Europe/London was supposed to switch when appropriate – Jesse Luke Orange Apr 20 '22 at 14:17
  • Yes, that one switches - but GMT doesn't. GMT does not know of any DST. – CBroe Apr 20 '22 at 14:22
  • So in my case do you know why certain dates seem to lose an hour? – Jesse Luke Orange Apr 20 '22 at 14:25
  • What date do you see in database? and What timezone your mysql server is running? from your scenario carbon seems to convert a GMT date from DB to British timezone. – Rudra Apr 20 '22 at 15:14
  • I changed the field to a datetime - 2022-04-04 00:00:00 – Jesse Luke Orange Apr 20 '22 at 15:17
  • If the user is picking a date then the database field should remain a date, not a datetime. The conversion errors you are demonstrating happen when you assume a date-only value should be treated as a timestamp (a date and time on that day). If you keep them as date-only strings from user input all the way through to your backend, they will never be affected by time zones. For further advices, please update your question to show how you are retrieving the input on the front-end, and how you are sending it to your PHP code. Thanks. – Matt Johnson-Pint Apr 20 '22 at 16:32
  • https://www.timeanddate.com/time/gmt-utc-time.html – Sammitch Apr 20 '22 at 17:53

2 Answers2

0

Dates and times get very complicated very quickly.

Unix and Javascript have similar notions about the representation of both which does not allow for representations of dates prior to 01/01/1970. Modern relational databases OTOH do allow for this (some even handle oddities like the Gregorian/Julian change).

It looks like the stored value has been cast crudely to a Unix timestamp then Carbon has converted from this using GMT rather than Europe/London.

The timezone Europe/London has daylight savings time, while GMT does not - so they are only equivalent for the winter months. GMT is calculated slightly differently from UTC but for most pruposes they can be considered equivalent.

The timezone in my application is set within config/app.php like so

But is that what Carbon is using?

You should have your default timezone set in your PHP.ini (it causes a performance overhead if its not set - so always a good idea to set some value). You can override this in code using dadte_default_timezone_set(), in specific APIs and/or date+time strings.

Also, if you know something is going to convert a date string into a time value, then explicitly add a time after 2am.

symcbean
  • 47,736
  • 6
  • 59
  • 94
  • ... "the stored value has been cast crudely to a Unix timestamp" ... If indeed that's what the OP is doing, that's the source of the issue. Date-only values should not be assigned a time at all. That's why things like html's `` has its value returned as a string, not a `Date` object. (The JS `Date` object isn't a date, it's a timestamp.) – Matt Johnson-Pint Apr 20 '22 at 16:35
-1

I strongly suggest to store all dates in UTC as it's the only reliable way to avoid confusion.

With Javascript you can get the UTC value of a date input this way:

let d = new Date('2022-04-22');
let utcdate = d.toISOString();

You can convert it back in to the local time for the user in the browser by simply creating a new Date object:

let d = new Date(utcdate).toLocaleDateString();
Onki Hara
  • 270
  • 1
  • 9
  • Sorry, but the often given advice of "always use UTC" is shortsighted, and not applicable when working with date-only values. Also there's little alignment between this answer and the original question. – Matt Johnson-Pint Apr 20 '22 at 16:29
  • I concede your points - thanks for the feedback. – Onki Hara Apr 21 '22 at 11:21