21

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?

0

7 Answers 7

33

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).

6
  • 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, 2017 at 18:49
  • 4
    @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, 2017 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, 2017 at 19:53
  • 1
    In my case, I had to do $node->field_date->date, instead of $node->field_date->start_date. Jul 28, 2017 at 22:27
  • 4
    Props to this response as this is the most informative piece of Drupal documentation I've EVER found.
    – Ryan H
    Nov 8, 2017 at 21:18
9

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

$node = Node::load(2100);
$userTimezone = new DateTimeZone(date_default_timezone_get());
$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.

4
  • 1
    I removed my previous comment. DrupalDateTime doesn't adjust for Drupal user's timezone.
    – oknate
    Jun 14, 2017 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, 2017 at 17:49
  • Thanks for this. It really helped me with drupal.stackexchange.com/q/256490/2089.
    – colan
    Feb 26, 2018 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. Feb 26, 2018 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 );
3

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");
2
  • 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');` Sep 22, 2019 at 15:59
  • The best solution. Thanks so much! Mar 15, 2022 at 22:20
1

This code is working for me.

// set use timezone
userTimezone = new DateTimeZone(drupal_get_user_timezone());
// set gmt timezone
$gmtTimezone = new DateTimeZone('GMT');
// Take the timestamp.
$start_date = $val['field_start_time_value'];    
$startDateTime = new DateTime($start_date, $gmtTimezone);
$offset = $userTimezone->getOffset($startDateTime);
$startInterval = DateInterval::createFromDateString((string)$offset . 'seconds');
$startDateTime->add($startInterval);
$result = $startDateTime->format('Y-m-d H:i:s');
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 and acknowledge that you have read and understand our privacy policy and code of conduct.

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