Using ->set($value) where $value is out of a form (#type = 'date') doesn't work. The data doesn't validate.

What should I be doing?

Regards,
Florian

Comments

davidwhthomas’s picture

I was looking for the same thing, but unfortunately it appears in

date_entity_metadata_property_info_alter

That the "setter callback" is explicitly unset for some reason.

unset($property['setter callback']);

For that reason, it appears there's no way to set a date field value using the entity api and entity_metadata_wrapper - because no setter callback exists :(

DT

davidwhthomas’s picture

P.S Note, as a workaround, you can do this type of thing instead:

    // Get profile entity from wrapper
    $profile = $order_wrapper->commerce_customer_settings->value();
    // Set end date value
    $profile->field_end[LANGUAGE_NONE][0]['value'] = date("Y-m-d H:i:s", $end_timestamp);
    commerce_customer_profile_save($profile);
    // Set updated profile_id reference value on order
    $order->commerce_customer_settings[LANGUAGE_NONE][0]['profile_id'] = $profile->profile_id;
    commerce_order_save($order);
kevinob11’s picture

I'm able to use the set method if I pass it a unix timestamp as created by strtotime().

ralt’s picture

Status: Active » Closed (works as designed)

@davidwhtthomas: using [LANGUAGE_NONE] kinda beats the point of using wrappers.

@kevinob11: Awesome! Thanks a lot.

davidwhthomas’s picture

@ralt, not using the entity wrapper for the profile date field set was the point of that example.

Nevertheless, good to hear the date field may now be set using entity wrapper with a timestamp value.

I was using a slightly older version of the date module so wasn't able to set the field using a timestamp with that version.

fengtan’s picture

Looks like this also works. Not sure why though:

  $node->field_my_date->set(array(
    'value' => date('Y-m-d H:i:s'),
    'value2' => date('Y-m-d H:i:s'),
  ));
focal55’s picture

Issue summary: View changes

I was struggling with this tonight. Here is what worked for me:

<?php
$wrapper = entity_metadata_wrapper($foo_entity, $bar_bundle);
// Set the zero indexed delta.
$wrapper->field_my_date[0]->set(array(
    // Should be in unix timestamp format.
    'value' => $start_timestamp,
    'value2' => $end_timestamp,
  ));
?>

Using field_my_date[0] cleared the error I was seeing in watchdog "General error: 1366 Incorrect integer value: 'value' for column 'delta' at row 1". I am not sure why, but seems as though the insert query was using 'value' as the delta value.

The post by Alice Heathon also helped me discover my solutions.

thumb’s picture

And, if you have a date field without an end date, the following should work.

<?php
$wrapper = entity_metadata_wrapper($foo_entity, $bar_bundle);
$wrapper->field_my_date->set($timestamp);
?>
psychobyte’s picture

date fields have timezone information. Is there a way to get that as well?

jeff h’s picture

Status: Closed (works as designed) » Active

@psychobyte: I believe this field is not defined correctly inside the date module — field_info_field('your_date_field_name_here') returns an array of info one piece of which describes the columns associated with the field. This is currently incorrectly (I believe) stating that the date field has just one column: "value", whereas I think it should return timezone info as well as date_type.

Looking at a sample entity object containing this field, I see the following on my sample date field:

field_sample_date
    und
        0
            value: 1421195239
            timezone: Pacific/Auckland
            timezone_db: UTC
            date_type: datestamp

I see no way to set all that using entity metadata wrappers.

jeff h’s picture

Actually, the patch at https://www.drupal.org/node/1392472#comment-8403055 fixed the issue I was having.

glass.dimly’s picture

So also, if you're using entity_metadata_wrapper->set it will invoke the node_save hooks, and if you've got logic there that assumes a normal Drupal date object, it will fail because entity_metadata_wrapper->field_date->value() in the node_save validdate, submit, or rules-invoked functions will return a timestamp rather than a drupalish date.

I did this, it's a little verbose but it works.

   $collection_wrapper = entity_metadata_wrapper('field_collection_item', $field_collection);
   $closing_time_original = $collection_wrapper->field_order_closing_time->value()

    //handle when entity_metadata_wrapper hands us a timestamp on save.
    if ((string)(int)$closing_time_original == $closing_time_original) { //checks if this is timestampish by casting to an int and then to a string and seeing if it is then equal to itself.

      //when this is entity_metadata_wrapper('field_collection_item', $id)->save() field_collection is set
      if (isset($field_collection->field_order_closing_time['und'][0]['value'])) {
        $closing_time = $field_collection->field_order_closing_time['und'][0];
      }
      else{
        $closing_time = array('value' => date('Y-m-d H:i:s', $closing_time_original));
      }
    }
    else{
      $closing_time = $closing_time_original;
    }
texas-bronius’s picture

I am really confused by this. It appears that ->value() (and ->raw() for that matter) convert the date in the field to, what, site timezone maybe? But setting does not care about timezone. Here's a test:

  $before = $pmaster_wrapper->field_pm_dob->value();
  $pmaster_wrapper->field_pm_dob = $pmaster_wrapper->field_pm_dob->value();
  $after = $pmaster_wrapper->field_pm_dob->value();
  // $after - before = 18000;

I can see that entity_metadata_wrapper is presenting the date out of pmaster's field as a timestamp (1117602000), and by passing this same timestamp into the field wrapper set() sets the time back on retrieval (1117584000).

I think, therefore, the right way to set the field is to the retrieve it as UTC as it is stored in the db. (Incidentally, it's *stored* as a varchar like 2000-03-19T00:00:00) What's the best way either to retrieve the UTC timestamp or to offset it by the correct (assume site?) timezone? That is to say, this appears to work for me, offsetting for site's timezone of America/Chicago:

  $participant_wrapper->field_datetime_birth = $pmaster_wrapper->field_pm_dob->value() + 18000;

when copying one metadata wrapper date field to another.

hgoto’s picture

I met the same problem @texas-bronius pointed. This comment is for people who will meet the same issue for future. My workaround is as following.


// I want to set field_some_date with the value $value.

// We shouldn't use this because timestamp is changed in some conditions when set.
// $wrapper->field_some_date = $timestamp;
// $wrapper->save();

$field_info = $wrapper->field_some_date->info();
$field_lang = $field_info['langcode'];
$wrapper->raw()->field_some_date[$field_lang][0]['value'] = format_date($timestamp, 'custom', 'Y-m-d H:i:s', 'UTC');
$wrapper->save();

*EDITED*

It's better to explicitly set timezones. The following page may be useful for people who have the same trouble.

- http://drupal.stackexchange.com/questions/26948/programmatically-set-dat...

kir lazur’s picture

It's easy. In example of commerce line item set dates from-to.

$dt = new DateTime('2016-03-01');
$dt2 = new DateTime('2016-03-06');

$entity_wrapper = entity_metadata_wrapper('commerce_line_item', $line_item_id);

$entity_wrapper->field_date_from_to->set(array(
    'value' => $dt->format('Y-m-d'),
    'value2' => $dt2->format('Y-m-d'),
  ));

 $entity_wrapper->save();

You are free to receive dates from any other source, like submitted form etc...

texas-bronius’s picture

Status: Active » Closed (works as designed)

I've run a lot of manual tests just now in a cumbersome /admin/devel window :), and here's what I've come up with:

Assignment of Date Field values in a Entity Metadata Wrapper differs according to cardinality (how many date fields you have) and whether or not Collect End Date is enabled on the field's configuration. Also, always assign timestamp (epoch), and Drupal/Date will handle value conversion to what it wants to store in the database.

Generic note on cardinality

With cardinality of 1 (single value), use:
field = $date;
or
field->value = $date;
With cardinality of 2+ (multiple or unlimited), use:
field[n] = $date;
or
field[n]->value = $date;

The difference between assignment and assignment to ->value property are due to Collect End Date below.

Whether Collect End Date is enabled

If the date field configuration does not have Collect End Date checked, your wrapper receives assignment like:
field->$date;
But if Collect End Date is checked (field accepts both start and end dates):
field->value = $date;
field->value2 = $date; (or just don't set it if it's not required, but this is how to set it)

I recommend this format instead of the handbook suggested format for dates because that seems to apply only to updating existing nodes. The format I recommend works both on Create Node and Update Existing Nodes.

I found that by assigning a complete array('value' => $date, 'value2' => $date2), as opposed to separate assignments to the properties value and value2 on the wrapper individually as shown above, you will circumvent date string format conversion before storage and will blindly store the timestamp regardless of how it should be stored (and is expected to be retrieved, say, by Views). In this case, when an ISO Date field presents a timestamp to Views, your Views will not output the retrieved date value but the current date/time. Refresh and see it update with the current time.

Date string format to use when setting Entity Metadata Wrapper values

Always use epoch timestamp regardless of the field's configured Date Type. The wrapper or the date module or some spirits from the underworld take care of conversion from timestamp. Attempting to guess at the right, alternate format always ended in sudden death for me, regardless of the field's date type.

Given an existing content type "testtest" with three fields on it as referenced below, here's the test I ran in Devel /admin/devel Execute PHP block. These fields each Collect End Date. To try without Collect End Date checked, repeat the same below but instead of field->value = $startDatetime, use field = $startDatetime. Seriously.

(omit the wrapping PHP tags, as per the Devel module's instructions)

$newNode = entity_create('node', array(
    'type' => 'testtest',
    'nid' => 0,
    'uid' => 1,
    'title' => 'test date fields',
  ));
$wrapper = entity_metadata_wrapper('node', $newNode, array('bundle' => 'testtest'));
$startDateTime = new DateTime('@' . 21600);

// dpm($wrapper->field_testdate_epoch->value(), "date field epoch");
$wrapper->field_testdate_epoch->value = $startDateTime->getTimestamp();
// dpm($wrapper->field_testdate_epoch->value(), "date field epoch updated but not saved");

// dpm($wrapper->field_testdate_iso->value(), "date field iso");
$wrapper->field_testdate_iso->value = $startDateTime->getTimestamp();
// dpm($wrapper->field_testdate_iso->value(), "date field iso updated but not saved");

// dpm($wrapper->field_testdate_date->value(), "date field date");
$wrapper->field_testdate_date->value = $startDateTime->getTimestamp();
// dpm($wrapper->field_testdate_date->value(), "date field date updated but not saved");

dpm($wrapper->value(), 'node in progress not saved');
// $wrapper->save();
// dpm($wrapper->value(), "Fields after save");

For some visual aids for future of life, see the dpm output of these fields before $wrapper->save() and immediately after save (without actually doing another wrap or anything.. it's EntityMetadataWrapper magic!) attached as jpg to this issue queue.

I hope this helps!!

texas-bronius’s picture

kellyimagined’s picture

This thread here was helpful but run strtotime() on the value before setting and it will work as expected.

StarBoyX’s picture

The problem for me is that my date time field store data in database in not as timestamp,
but in function `entity_property_verify_data_type($data, $type)` the date is verified as an integer by code

   function entity_property_verify_data_type($data, $type) 
     ...
    case 'date':
    case 'duration':
    case 'integer':
      return is_numeric($data) && strpos($data, '.') === FALSE;
   ...

but date is string like 2019-06-05 00:00:00.
No something like case 'date_popup': check if is valid string date.