Skip to content

DatePicker: Timezone offset applied incorrectly when parsing dates with time #2798

@baeroe

Description

@baeroe

Description

The DatePicker component incorrectly applies timezone offsets when parsing dates with time enabled, causing the displayed time to shift by the user's timezone offset from UTC.

For example, if a user in UTC+1 timezone enters 14:00, the displayed value shifts to 15:00 after saving and reloading.

Root Cause

Two issues in frontend/js/components/DatePicker.vue:

1. dateFormat uses 'Z' (ISO 8601 with timezone)

dateFormat: (self.enableTime && self.noCalendar) ? 'H:i:S' : (self.enableTime ? 'Z' : 'Y-m-d'),

The 'Z' format outputs ISO 8601 dates with timezone information, but the backend stores and returns dates without timezone info. This mismatch causes parsing issues.

2. parseDate incorrectly adds 'Z' suffix

return parse(date + 'Z', fullFormat + 'X', Date.UTC());

This appends a 'Z' (UTC indicator) to dates that are actually in local time, causing the timezone offset to be applied twice.

Proposed Fix

  1. Change dateFormat to use explicit format without timezone:
dateFormat: (self.enableTime && self.noCalendar) ? 'H:i:S' : (self.enableTime ? 'Y-m-d H:i:S' : 'Y-m-d'),
  1. Simplify parseDate to not apply UTC conversions:
parseDate: function (date, format) {
  // European format: dd.MM.yyyy HH:mm:ss
  if (/^\d{2}\.\d{2}\.\d{4} \d{2}:\d{2}:\d{2}$/.test(date)) {
    // Manual parsing for European format
    var parts = date.split(' ');
    var datePart = parts[0].split('.');
    var timePart = parts[1].split(':');
    return new Date(datePart[2], datePart[1] - 1, datePart[0], timePart[0], timePart[1], timePart[2]);
  }
  // ... similar for other European formats
  
  // ISO formats - date-fns v1 parse() handles these correctly
  if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(date)) {
    return parse(date);
  }
  // ... etc
  
  return new Date(date);
}

Environment

  • Twill version: 3.5.2
  • PHP version: 8.2
  • Browser: Chrome (also reproducible in Firefox/Safari)
  • Timezone: Europe/Berlin (UTC+1)

Steps to Reproduce

  1. Set your system timezone to something other than UTC (e.g., UTC+1)
  2. Create a model with a datetime field using DatePicker with enableTime: true
  3. Enter a time value (e.g., 14:00)
  4. Save the record
  5. Reload the page
  6. Observe the time has shifted by your timezone offset (shows 15:00 instead of 14:00)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions