3

I want to get the date of last monday at 00:00:00 in a specific timezone. When I try this:

DrupalDateTime::createFromFormat('U', strtotime('last week monday'))->setTime(0, 0, 0)->setTimezone(new \DateTimeZone('America/New_York'))->render()

The result is:

2022-06-12 20:00:00 America/New_York

Which is actually Sunday at 10 PM.

How can I get it to return 2022-06-13 00:00:00 America/New_York instead?

This "corrects" what I was seeing, but feels wrong:

    date_default_timezone_set('America/New_York');
    $from = DrupalDateTime::createFromFormat('U', strtotime('last week monday'))->getTimestamp();
    $to = DrupalDateTime::createFromFormat('U', strtotime('last monday'))->getTimestamp();

This gives me:

June 13th, 2022 12:00 AM
June 20th, 2022 12:00 AM

Which is last week monday through last monday (this week). Is calling ->format() setting the timezone offset twice, perhaps?

If I format this timestamp using:

DrupalDateTime::createFromFormat('U', $returned_value)->format('F j, Y H:i:s e')

I get this on the screen:

June 13, 2022 04:00:00 +00:00

The server is in UTC time. I simply want the timestamp of a date, without offsets applied (but correctly formatted to the user).

Edit:

The objective is to query for entities in Drupal created in the last week (starting on Monday), at midnight (00:00:00) through the following Monday at midnight, in this specific timezone.

1
  • I'm thinking you probably have to set the timezone before the time. It's only a hypothesis though.
    – Jaypan
    Jun 25 at 1:41

3 Answers 3

4

I can see nothing unexpected. Midnight in UTC is 8 PM the day before in New York, at least in the summer time.

Timestamps are always UTC. This is the reason why your second attempt

$from = DrupalDateTime::createFromFormat('U', strtotime('last week monday'))->getTimestamp();

can be reduced to

$from = strtotime('last week monday');

without changing the result.

Original question

How can I get it to return 2022-06-13 00:00:00 America/New_York instead?

In this case don't convert to UTC timestamps. You can specify the timezone (the Drupal enhanced DrupalDateTime object accepts the timezone as string) when creating the object:

$from = (new DrupalDateTime('last week monday', 'America/New_York'))->render();

results in

"2022-06-13 00:00:00 America/New_York"

Which will internally use a different timestamp than before, but this is only relevant if you convert it to a different timezone, for example to store it in the database or use it in a query against stored values.

Updated OP in the comments

The goal is to query for nodes created Monday to Monday, which only happens in this timezone in EDT.

The Created field in nodes is a timestamp field, so in this case you need the UTC timestamp:

$from = (new DrupalDateTime('last week monday', 'America/New_York'))->getTimestamp();

As you have already noticed in the second attempt, using legacy PHP date_default_timezone_set()/strtotime(), this will be indeed 4 hours ahead of New York time.

6
  • Is it fair to say entity created/changed timestamps are in UTC, then? For some reason I thought the Regional date setting in the admin may have affected this.
    – Kevin
    Jun 25 at 12:33
  • Updated OP. The goal is to query for nodes created Monday to Monday, which only happens in this timezone in EDT. No storage or saving.
    – Kevin
    Jun 25 at 12:55
  • Yes, this is fair to say. The regional settings affect how dates are entered and displayed. Storage is always UTC, also for complex date fields.
    – 4uk4
    Jun 25 at 13:17
  • In this case the mentioned internal timestamp is exactly what you are looking for, see the edit.
    – 4uk4
    Jun 25 at 13:18
  • @Kevin Unix timestamps by definition are the number of seconds (minus leap second) since 00:00:00 UTC on 1 January 1970. Internally, Drupal does everything in UTC ; time zones are an input/output thing.
    – mpdonadio
    Jun 25 at 18:51
1

I tried different snippets, but they all output a time for June 12, not for June 13.

echo DrupalDateTime::createFromFormat('U', strtotime('last week monday'), new \DateTimeZone('America/New_York'))
  ->setTime(0, 0, 0)
  ->render(), "\n";
// 2022-06-12 00:00:00 +00:00
echo DrupalDateTime::createFromFormat('U', strtotime('last week monday'))
  ->setTimezone(new \DateTimeZone('America/New_York'))
  ->setTime(0, 0, 0)
  ->render(), "\n";
// 2022-06-12 00:00:00 America/New_York
echo DrupalDateTime::createFromTimestamp(strtotime('last week monday'), new \DateTimeZone('America/New_York'))
  ->setTime(0, 0, 0)
  ->render(), "\n";
// 2022-06-12 00:00:00 America/New_York
echo DrupalDateTime::createFromTimestamp(strtotime('last week monday'), new \DateTimeZone('America/New_York'))
  ->render(), "\n";
// 2022-06-12 18:00:00 America/New_York

The only way to get the expected result is to set the default timezone with date_default_timezone_set(). In that case, the following code outputs the expected result.

date_default_timezone_set('America/New_York');
echo DrupalDateTime::createFromTimestamp(strtotime('last week monday'))->render(), "\n";
// 2022-06-13 00:00:00 America/New_York

Notice that isn't necessary to call setTime(0, 0, 0), since strtotime('last week monday') returns a timestamp for midnight of last week Monday. (It can be verified with echo date("F j, Y, g:i a", strtotime("last week monday")), "\n"; which outputs June 13, 2022, 12:00 am.)

1

DrupalDateTime::createFromFormat() is basically a wrapper for PHP's createFromFormat.

A search of the PHP docs reveals this comment:

createFromFormat('U') has a strange behaviour: it ignores the datetimezone and the resulting DateTime object will always have GMT+0000 timezone.

So, PHP bug or feature, you decide.

To create a DateTime object with a time based on a string, as a general approach:

  1. Instantiate the DateTime object with the timezone you want.
  2. Change the object's time.

Source: I spent the past two years working on some scheduling software implemented in Drupal that does a lot of date manipulation, and ran into tons of gotchas like this.

EDIT

The objective is to query for entities in Drupal created in the last week (starting on Monday), at midnight (00:00:00) through the following Monday at midnight, in this specific timezone.

Drupal always stores dates in the database as UTC. So if you are doing an EntityQuery, here is one way to do it:

  1. Create a DateTime object. Set the timezone to the desired timezone.
  2. Set the DateTime to the desired date and time.
  3. Change the DateTime's timezone to UTC: $datetime->setTimeZone(timezone_open(DateTimeItemInterface::STORAGE_TIMEZONE));
  4. Get a proper string from the DateTime: $datetime->format((DateTimeItemInterface::DATETIME_STORAGE_FORMAT).
  5. Run the EntityQuery with that string.
2
  • Yes, this kind of date stuff has always been a blind spot for me. I was trying to use the Drupal wrappers thinking it may be easier, wound up doing the opposite. I also thought for some reason the timezone setting in the application affected this, but that does not seem to be the case.
    – Kevin
    Jun 25 at 12:54
  • The Drupal (site) timezone setting has basically no effects. I don't use the Drupal wrappers but I do use the constants (see the update to my answer) to ensure I don't accidentally set things incorrectly. Jun 25 at 13:49

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.