Problem/Motivation

  1. #2824717: Add a format constraint to DateTimeItem to provide REST error message added a validation constraint. This already improves the REST DX (== RX). Because now REST clients are at least told what they're doing wrong.
  2. But we should go further: we should allow POSTing and PATCHing with datetime fields having values in any timezone.

Proposed resolution

Do something similar to #2768651: Let TimestampItem (de)normalize to/from RFC3339 timestamps, not UNIX timestamps, for better DX:

  1. implement a DateTimeNormalizer class
  2. with normalization to/denormalization from RFC3339 timestamps

This would improve/unify the (de)normalization of both DateTimeItem and DateRangeItem fields.

Remaining tasks

TBD

User interface changes

None.

API changes

None.

Data model changes

None.

Comments

Wim Leers created an issue. See original summary.

wim leers’s picture

Title: Let DateTimeItem denormalize from ISO8601 timestamps *with* timezones, for better DX » [PP-1] Let DateTimeItem denormalize from ISO8601 timestamps *with* timezones, for better DX
Status: Active » Postponed
mpdonadio’s picture

Thought about this one the commute home tonight.

We have #2716891: DateTimeIso8601::setDateTime() vs. DateTimeItem::schema(): (at least) one of them is broken, which aims to solve a similar problem, but not for REST. That issue would change the storage format to include +00:00 (explicit UTC) and potentially handle conversion to UTC in the setter. I don't see these as mutually exclusive problems anymore. In fact, I think having a denormalizer for these fields that behave in the exact same manner as timestamps (permissive to common input formats) is good (1) for consistency and (2) isolates interface from implementation. The denormalizer will help future proof any changes to field storage internals.

I also think it would be beneficial to add a normalize (with BC) the responses to the same \DateTime::RFC3339 format, again for consistency. Again, the outside world doesn't need to care about how we store stuff and all timestamps/dates in responses would be the same and we can mess with internals w/o breaking APIs.

wim leers’s picture

isolates interface from implementation

+1!

The denormalizer will help future proof any changes to field storage internals.

+1! I'd like to add: it's okay to expose "default Entity/Field API internal behavior by default", but it's not ideal. Not every REST client has the ability to step through PHP code with Xdebug. So we should A) give error feedback appropriate for REST clients, B) try to meet expectations of "HTTP developers", not "PHP developers".

So, I wholeheartedly agree with #3, and especially with its conclusion, which explains it very well:

I also think it would be beneficial to add a normalize (with BC) the responses to the same \DateTime::RFC3339 format, again for consistency. Again, the outside world doesn't need to care about how we store stuff and all timestamps/dates in responses would be the same and we can mess with internals w/o breaking APIs.

mpdonadio’s picture

Title: [PP-1] Let DateTimeItem denormalize from ISO8601 timestamps *with* timezones, for better DX » Let DateTimeItem denormalize from ISO8601 timestamps *with* timezones, for better DX
Status: Postponed » Active

#2824717: Add a format constraint to DateTimeItem to provide REST error message is in (which formally blocked this), and #2768651: Let TimestampItem (de)normalize to/from RFC3339 timestamps, not UNIX timestamps, for better DX is in (which didn't block, but will provide guidance), so I think we can work on this again.

Since we don't have the constraint(s) on DateRangeItem, we should keep the scope of this to DateTimeItem.

For date+time items, the patch should be able to mirror the timestamp one (accept a few formats inbound, outbound ISO w/ TZ).

For date-only, I am not sure if we really need any change?

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

mpdonadio’s picture

So, I started to work on this, based off of the work in #2768651: Let TimestampItem (de)normalize to/from RFC3339 timestamps, not UNIX timestamps, for better DX.

In that patch, we have a Normalizer in serialization and hal. So, to prevent cross-contamination and to allow people to not have to have HAL enabled, we need two Normalizer services in Drupal\datetime\Normalizer, with the appropriate priorities?

wim leers’s picture

So, to prevent cross-contamination and to allow people to not have to have HAL enabled, we need two Normalizer services in Drupal\datetime\Normalizer, with the appropriate priorities?

Correct, that'd be in line with what #2768651: Let TimestampItem (de)normalize to/from RFC3339 timestamps, not UNIX timestamps, for better DX did.

However… we realized in #2901429: JSON API Extras @ResourceFieldEnhancer plugins should also be applied to filter values and #2860350: Document why JSON API only supports @DataType-level normalizers that adding @FieldType-plugin-level normalizers is a bad idea, because they need to be written for not only serialization.module (default normalization) and hal.module (HAL normalization), but also jsonapi.module (JSON:API normalization) and any other alternative normalization. So that is unfortunate.

The solution lies in not adding more @FieldType-plugin-level normalizers, but instead working at the @DataType-plugin level. Because then it's automatically picked up by all normalizations.

wim leers’s picture

So based on #8, we'd want this issue to add a \Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601 normalizer. Then it'll work everywhere automatically, in all 3 normalizations mentioned in #8 :)

wim leers’s picture

Title: Let DateTimeItem denormalize from ISO8601 timestamps *with* timezones, for better DX » Let DateTimeItem (de)normalize from ISO8601 timestamps *with* timezones, for better DX
Issue tags: +Contributed project blocker, +Needs tests
Related issues: +#2870609: [PP-1] Core Date field is serialized as String instead of timestamp/long

We should also look at the normalization of fields configured to be "date-only". That would solve #2870609: [PP-1] Core Date field is serialized as String instead of timestamp/long.

wim leers’s picture

Priority: Normal » Major

Without this, and especially without test coverage, how to deal with DateTimeItem fields via core's REST API and contrib's JSON API is ill-defined, and may change without us knowing (== BC break).

Bumping to major for that reason.

wim leers’s picture

Title: Let DateTimeItem (de)normalize from ISO8601 timestamps *with* timezones, for better DX » API-First DX of "date" and "date range" fields: add 'datetime_iso8601' @DataType (de)normalizer using RFC3339 timestamps with timezones

If this would do what #8 + #9 say, then #2918554: [PP-1] Let DateRangeItem (de)normalize to/from RFC3339 becomes a duplicate.

Because:

\Drupal\datetime\Plugin\Field\FieldType\DateTimeItem
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties['value'] = DataDefinition::create('datetime_iso8601')
      ->setLabel(t('Date value'))
      ->setRequired(TRUE);

    $properties['date'] = DataDefinition::create('any')
      ->setLabel(t('Computed date'))
      ->setDescription(t('The computed DateTime object.'))
      ->setComputed(TRUE)
      ->setClass('\Drupal\datetime\DateTimeComputed')
      ->setSetting('date source', 'value');

    return $properties;
  }

The only non-internal field here is value, which uses the datetime_iso8601 @DataType plugin.

\Drupal\datetime_range\Plugin\Field\FieldType\DateRangeItem
  public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
    $properties['value'] = DataDefinition::create('datetime_iso8601')
      ->setLabel(t('Start date value'))
      ->setRequired(TRUE);

    $properties['start_date'] = DataDefinition::create('any')
      ->setLabel(t('Computed start date'))
      ->setDescription(t('The computed start DateTime object.'))
      ->setComputed(TRUE)
      ->setClass(DateTimeComputed::class)
      ->setSetting('date source', 'value');

    $properties['end_value'] = DataDefinition::create('datetime_iso8601')
      ->setLabel(t('End date value'))
      ->setRequired(TRUE);

    $properties['end_date'] = DataDefinition::create('any')
      ->setLabel(t('Computed end date'))
      ->setDescription(t('The computed end DateTime object.'))
      ->setComputed(TRUE)
      ->setClass(DateTimeComputed::class)
      ->setSetting('date source', 'end_value');

    return $properties;
  }

The only non-internal fields here are value and end_value, both of which also use the datetime_iso8601 @DataType plugin.

wim leers’s picture

Issue summary: View changes

IOW: adding a \Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601 (de)normalizer would fix the DX of both field types!

wim leers’s picture

wim leers’s picture

Title: API-First DX of "date" and "date range" fields: add 'datetime_iso8601' @DataType (de)normalizer using RFC3339 timestamps with timezones » [PP-1] API-First DX of "date" and "date range" fields: add 'datetime_iso8601' @DataType (de)normalizer using RFC3339 timestamps with timezones
Status: Active » Postponed

Over at #2926508-18: Add DateTimeNormalizer+TimestampNormalizer, deprecate TimestampItemNormalizer: @DataType-level normalizers are reusable by JSON:API, I'm proposing to deal with several issues at the same time, because it turns out there's a single elegant solution that will fix it all! For now, just postponing this, and not marking it as a duplicate.

mpdonadio’s picture

Just read other patch, and agree with #15.

wim leers’s picture

Great :)

Let's keep this open for now, until #2926508: Add DateTimeNormalizer+TimestampNormalizer, deprecate TimestampItemNormalizer: @DataType-level normalizers are reusable by JSON:API matures a bit more. Perhaps we'll want to reduce scope there, and move part of that patch to this issue.

wim leers’s picture

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

wim leers’s picture

Title: [PP-1] API-First DX of "date" and "date range" fields: add 'datetime_iso8601' @DataType (de)normalizer using RFC3339 timestamps with timezones » API-First DX of "date" and "date range" fields: add 'datetime_iso8601' @DataType (de)normalizer using RFC3339 timestamps with timezones
Status: Postponed » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.