18

I have a Datetime Range field (field_date) in a content type. Once I create my content type I set the Start Date as:

2017-02-27 19:30:01

enter image description here

Now I want to get the value and show the date in other format, so try to use the following code:

// Loading the node.
$node = Node::load(2100);
// Getting the start date value.
$date = $node->field_date->value;
// Printing to see what is the output.
dpm($node->field_date->value);
$date = strtotime($date);
// Printing my timezone.
dpm(drupal_get_user_timezone());
// Applying my custom format.
$date = \Drupal::service('date.formatter')->format($date, 'custom', 'Y-m-d H:i:s', drupal_get_user_timezone());
// Printing to see the output.
dpm($date);

This is my output:

// It's fine.
2017-02-28T00:30:01
// Its fine.
America/New_York
// This is not what I waiting for. I'm waiting here: 2017-02-27 19:30:01
2017-02-28 00:30:01

So, how to show a date in the correct timezone?

20

I suspect the problem has to do with some of the nuances of strtotime().

strtotime() can take just about any string, and convert it into a Unix timestamp. The problem is that when the string doesn't contain an explicit timezone, it will use what is set by date_default_timezone_get(). That should be set from the current user, though (it is set by the account proxy). However, the string that you are pulling out of $node->field_date->value is implicitly in UTC. In other words, I think the string is being parsed by strtotime() is being interpreted as in 'America/New_York' and not 'UTC'.

The good news is that you can simplify your situation a lot. The field item for a date range consists of four parts

  • 'value' is the start date as a string in UTC
  • 'start_date' is a DrupalDateTime object representing 'value'
  • 'end_value' is the end date as a string in UTC
  • 'end_date' is a DrupalDateTime object representing 'end_value'

For a plain date time field they are

  • 'value' is the date as a string in UTC
  • 'date' is a DrupalDateTime object representing 'value'

So, you can work with the DrupalDateTime() object directly. In addition, that date.formatter service should pull in the proper timezone in use by the user viewing the page by default (in addition to using the user's active language).

I suspect something like this will work

$node = Node::load(2100);
$start_date = $node->field_date->start_date;
$formatted = \Drupal::service('date.formatter')->format(
  $start_date->getTimestamp(), 'custom', 'Y-m-d H:i:s P'
);

Note I added the 'P' format placeholder in there so you can see what what timezone the system thinks is in use.

Make sure you have the proper timezone set up on admin/config/regional/settings, and that your user timezone is what you are expecting. Also ensure that you have the proper timezone set in your php.ini (it's the date.timezone setting); weird things happen when this isn't set (And off the top of my head, I don't recall if that causes a warning in the installer or on the status report when not set. I remember the issue, but not whether it got committed).

  • 1
    @mpdonadio Thanks for the help! I did not know about the date feature to access the DrupalDateTime. Out of curiosity how did you figure this out? I was able to figure out that my field was of the type DateTimeItem but within there it doesn't seem obvious that there is a date (or value) public member for that class. – Pzanno Apr 28 '17 at 18:49
  • 1
    @Pzanno I'm the Drupal 8 core datetime.module co-maintainer, and also a lot of other core date/time related stuff comes my way :) The properties are magic methods. If you look at DateTimeItem::propertyDefinitions(), you will see what is exposed. Then look at DateTimeComputed::getValue() to see how that works. All Item classes can be examined that way, but only a handful use computed values. I wish there was a better way to expose this to the API. – mpdonadio Apr 28 '17 at 19:15
  • @mpdonadio thanks for the explanation! I missed the fact that they were even defined in DateTimeItem::propertyDefinitions(). I figured there was some magic methods happening, but wanted to understand how these were created so I could better understand how to access other fields as well. Like you said it would be nice if the API was exposed, but this helps. Thanks again. – Pzanno Apr 28 '17 at 19:53
  • 1
    In my case, I had to do $node->field_date->date, instead of $node->field_date->start_date. – Marcos Buarque Jul 28 '17 at 22:27
  • 2
    Props to this response as this is the most informative piece of Drupal documentation I've EVER found. – Ryan H Nov 8 '17 at 21:18
6

Based in Handling timezone conversion with PHP DateTime I modified my code to this:

$node = Node::load(2100);
$userTimezone = new DateTimeZone(drupal_get_user_timezone());
$gmtTimezone = new DateTimeZone('GMT');
$myDateTime = new DateTime($node->field_date->value, $gmtTimezone);
$offset = $userTimezone->getOffset($myDateTime);
$myInterval = DateInterval::createFromDateString((string)$offset . 'seconds');
$myDateTime->add($myInterval);
$result = $myDateTime->format('Y-m-d H:i:s');
dpm($result);

And now my output date it's fine:

2017-02-27 19:30:01

This works. But I'm sure that there is a way to do this using the Drupal API.

  • 1
    I removed my previous comment. DrupalDateTime doesn't adjust for Drupal user's timezone. – oknate Jun 14 '17 at 20:03
  • In my case I just used this before any date function call: date_default_timezone_set(drupal_get_user_timezone()); – Roger Aug 24 '17 at 17:49
  • Thanks for this. It really helped me with drupal.stackexchange.com/q/256490/2089. – colan Feb 26 '18 at 15:43
  • @colan a pleasure help you, if you think that my answer is useful I think that you can check the other answer too. – Adrian Cid Almaguer Feb 26 '18 at 15:50
4

I usually solve it this way.:

node = Node::load(1);    
$date_original= new DrupalDateTime( $node->field_date->value , 'UTC' );     
$result = \Drupal::service('date.formatter')->format( $date_original->getTimestamp(), 'custom', 'Y-m-d H:i:s'  );    
dpm( $result );
2

This worked for me:

// Take the timestamp.
$timestamp = $form_state->getValue($form_field)->getTimestamp();
// Convert to UTC - the way its saved in database.
$date = DrupalDateTime::createFromTimestamp($timestamp, 'UTC');
// Add on the current entity.
$this->entity->{$db_field}->value = $date->format("Y-m-d\TH:i:s");
  • It works for me as well ` $dDate = DrupalDateTime::createFromTimestamp($form_state->getValue('service_date')->getTimestamp(), 'UTC'); // Use like this. $dDate->format('Y-m-d\TH:i:s');` – Yogesh Kushwaha Sep 22 at 15:59
0

None of these other answers worked for me. This is what I finally ended up with:

$range_start = DrupalDatetime::createFromTimestamp((int)$start_date); // unix timestamp
$range_start->setTimezone(new \Datetimezone('EST'));
$node->field_date_range->value = format_date(
  $range_start->getTimestamp() , 'custom', 'Y-m-d\TH:i:s', 'EST');
0

I use this snippet to work with dates :

<?php
class MyApp extends ControllerBase
{
    public function output() 
    {
        $dtime = DateTime::createFromFormat("d/m/Y - H:I:s", "22/12/1999 - 22:55:12", $this->getDateTimeZone());
        $time = $dtime->format('r');
        // My code goes here
    }

    private function getDateTimeZone()
    {
        return new DateTimeZone(drupal_get_user_timezone());
    }
}

You can get more informations about the Datetime object here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.