Thank you so much for your work on this widget. It is a vast improvement on Drupal's core functionality.

I am working on a site where a content editor can create events that can happen anywhere in the world, and they need to ability to specify the local time for that event. For example, New York Fashion Week starts at 8am in New York, and London Film Festival starts at 8am in London. The timezone needs to match the location of the event, not the location of the content editor (which is largely irrelevant).

Do you have any plans to enable the option of a timezone select list within the field widget? And the storing of the desired timezone with the timestamp?

Comments

Chris Gillis created an issue. See original summary.

mandclu’s picture

There's already timezone handling built into Smart Date, so it would really be mostly a case of exposing it in the UI, with field config to control whether or not it appears.

That said, times are stored as timestamps, so on display the time would be converted to the site's time, or the user timezone.

If you also store the location there might be a way to convert it to local time, but it seems like it would be custom.

Chris Gillis’s picture

That's exactly the point. The site's time and the user's timezone are both irrelevant in this case. When your events are happening at different times around the world, the desired timezone must be stored with the timestamp, as a pair. Then, the date formats can expose that in the output.

For another example, think about an airlines booking site. A person who lives in London books a flight from NYC to LA. The flight departs at a certain time in EST, and arrives at a certain time in PST. Both the departure timestamp and the arrival timestamp should be stored in conjunction with a timezone. If I output both timestamps using BST (London's time zone) they would be incorrect.

laura.gates’s picture

StatusFileSize
new54.18 KB

I agree that there needs to be an option to choose time zones with Smart Date. My organization has multiple campuses in different time zones. Oftentimes, we well have a a meeting/event on one campus and it's Video teleconferenced to the other campuses. In Drupal 7 with the Date field we add the date twice so that we can display the two different time zones for the meeting. This also helps with allowing users to filter by their time zone.

mandclu’s picture

Actually, thinking about this some more, it would need to be a storage-level setting, since the current schema doesn't allow for storing a timezone.

Chris Gillis’s picture

StatusFileSize
new3.79 KB

Here's a first pass. Timezone being stored. Includes a new widget for date with timezone selector.

Issues still outstanding:
* Widget styling could improve.
* Changing the timezone currently does not affect the timestamp being stored. It probably should. For instance if you set the date to 2000-01-01 and the timezone to UTC and hit save, it will store a timestamp of 946684800. Then if you change the timezone to "Pacific/Wallis" and hit save again (without changing the date), it SHOULD add twelve hours to that UTC timestamp being stored... however currently it doesn't. That's fine for my needs right now, but should probably be fixed in future.

mandclu’s picture

Thanks for your work on this!

It's my understanding that timestamps should always be UTC, so I think the current behaviour in your patch (just adding a timezone) is fine.

My preference would be to make this a storage-level configuration. I think there are lots of use cases where it isn't necessary to use anything other than the site's timezone, so I wouldn't want to make it necessary to have this as an extra piece of configuration if it's not needed.

According to https://www.drupal.org/node/3009387 drupal_get_user_timezone has been replaced in Drupal 8.8, so to keep things compatible we should probably have a check if drupal_get_user_timezone is defined, and if not use date_default_timezone_get.

I also just came across #2632040: [PP-1] Add ability to select a timezone for datetime field so I've added that as a related issue. If similar functionality might be added to core at some point, we should consider if there are conventions there we should adopt, or potentially improve upon. There appears to be years' worth of discussion about similar issues, at least.

mandclu’s picture

A couple of other thoughts:

We should also consider if there's a way to leverage one of these modules instead of rolling our own functionality:
https://www.drupal.org/project/tzfield
https://www.drupal.org/project/datetime_range_timezone

Also, at some point we should probably move this to being a patch against the 8.x-2.x branch, but until that's closer to stable I wouldn't call that a priority.

mandclu’s picture

@laura.gates wouldn't it make more sense to just have it able to display a single date/time in the user's time zone?

It seems to me that the use case for having a different time zone per value on a multivalue field would be an edge case, but open to other ideas.

Chris Gillis’s picture

@laura.gates it sounds like what you want is a single time stored, but a new formatter that outputs the time in a range of different timezones. E.g. If we store 8am UTC, then you want that singe date to be displayed as 3am in NYC and 11am in Moscow, for instance. A multi-timezone formatter wouldn't be too hard, but I recommend opening a separate issue for that.

laura.gates’s picture

@manclu I'm getting the following error when applying the patch:

Notice: Undefined property: stdClass::$field_event_date_timezone in Drupal\Core\Entity\Sql\SqlContentEntityStorage->loadFromDedicatedTables() (line 1289 of core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php).

I also tested with simpletest.me and don't see an option to set a timezone.

Edit: If I try to change the widget in my content type, I get:

The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">Drupal\Core\Database\DatabaseExceptionWrapper</em>: SQLSTATE[42S22]: Column not found: 1054 Unknown column &#039;field_event_date_timezone&#039; in &#039;where clause&#039;: SELECT 1 AS expression
FROM
{node_revision__field_event_date} t
WHERE (field_event_date_value IS NOT NULL) OR (field_event_date_end_value IS NOT NULL) OR (field_event_date_duration IS NOT NULL) OR (field_event_date_timezone IS NOT NULL)
LIMIT 1 OFFSET 0; Array

Same error on both of the Drupal 8.7 sites that I work on.

mandclu’s picture

Thinking more about this, maybe what would make most sense would be to add the extra field to the schema, but then make the form element visible or not based on the field's configuration. I work on a lot of sites where being able to override the timezone just isn't necessary, but I could see allowing that as an option. If enabled, I could being able to set the default to be:
- none (nothing stored, so assumed to be site default)
- site default
- user's timezone (from their user setting)

In a similar way, we should probably provide more options on the display side:
- display as stored (site default, if nothing stored)
- display in user's timezone
- display as site default
- display using a manually specified timezone

There should probably be an option to display the timezone.

laura.gates’s picture

@manclu, I like your way of thinking for this. Both display in user's timezone and/or a manually specified timezone should work just fine for my use case.

Chris Gillis’s picture

StatusFileSize
new3.81 KB

Turns out giving a hidden field a set of nested options is a bad idea. Adjusted.

Also, the timezone selector seems to be killing the end date functionality.

I don't recommend anyone use these patches yet. They are not production ready, just a start. I mainly need to see the right storage happening for now, and I'll fix the widget / formatter later (if I get around to it).

Chris Gillis’s picture

StatusFileSize
new3.81 KB

Whoops, corrupt file.

mandclu’s picture

Version: 8.x-1.x-dev » 8.x-2.x-dev
Status: Active » Needs review
StatusFileSize
new12.98 KB

Here's an updated patch, against the 8.x-2.x branch. This seems to be working to allow a user to specify a timezone, and enter/edit times and dates relative to that timezone. In this version, if a timezone is provided, the date will always be output in the specified timezone (the timezone itself is output by the time format string in the chosen Smart Date Format).

I did allow an option to set the default for the timezone widget to be:
- nothing (uses the site default)
- the site or user timezone (always stored, unless manually switched to the null/site default as above)
- a manually specified timezone (always saved, same as above)

So far there isn't a lot of complexity on the output side. I could see adding an option to also output the date range in the user/site default timezone if a timezone is specified, or maybe that should be the default?

laura.gates’s picture

Hey Mandclu!

Thanks so much for working on this. So I tested this on 8.x-2 dev and alpha 3. I'm getting DB errors when I save a node with the field display set to include timezone. (I like how it looks btw, good job).

I'm getting a DB error on this:
Notice: Undefined property: stdClass::$field_event_date_rrule in /var/www/html/docroot/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php on line 1289

This doesn't seem to matter if I enter only one date value or multiple. Below is the full error message:

The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">Drupal\Core\Entity\EntityStorageException</em>: SQLSTATE[42S22]: Column not found: 1054 Unknown column &#039;field_event_date_rrule&#039; in &#039;field list&#039;: INSERT INTO {node__field_event_date} (entity_id, revision_id, bundle, delta, langcode, field_event_date_value, field_event_date_end_value, field_event_date_duration, field_event_date_rrule, field_event_date_timezone) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9), (:db_insert_placeholder_10, :db_insert_placeholder_11, :db_insert_placeholder_12, :db_insert_placeholder_13, :db_insert_placeholder_14, :db_insert_placeholder_15, :db_insert_placeholder_16, :db_insert_placeholder_17, :db_insert_placeholder_18, :db_insert_placeholder_19); Array
(
    [:db_insert_placeholder_0] =&gt; 2172383
    [:db_insert_placeholder_1] =&gt; 14926163
    [:db_insert_placeholder_2] =&gt; event
    [:db_insert_placeholder_3] =&gt; 0
    [:db_insert_placeholder_4] =&gt; en
    [:db_insert_placeholder_5] =&gt; 1578435420
    [:db_insert_placeholder_6] =&gt; 1578439020
    [:db_insert_placeholder_7] =&gt; 60
    [:db_insert_placeholder_8] =&gt; 
    [:db_insert_placeholder_9] =&gt; 
    [:db_insert_placeholder_10] =&gt; 2172383
    [:db_insert_placeholder_11] =&gt; 14926163
    [:db_insert_placeholder_12] =&gt; event
    [:db_insert_placeholder_13] =&gt; 1
    [:db_insert_placeholder_14] =&gt; en
    [:db_insert_placeholder_15] =&gt; 1578409200
    [:db_insert_placeholder_16] =&gt; 1578412800
    [:db_insert_placeholder_17] =&gt; 60
    [:db_insert_placeholder_18] =&gt; 
    [:db_insert_placeholder_19] =&gt; Africa/Asmara
)
 in <em class="placeholder">Drupal\Core\Entity\Sql\SqlContentEntityStorage-&gt;save()</em> (line <em class="placeholder">847</em> of <em class="placeholder">core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php</em>). <pre class="backtrace">Drupal\Core\Database\Statement-&gt;execute(Array, Array) (Line: 631)
Drupal\Core\Database\Connection-&gt;query(&#039;INSERT INTO {node__field_event_date} (entity_id, revision_id, bundle, delta, langcode, field_event_date_value, field_event_date_end_value, field_event_date_duration, field_event_date_rrule, field_event_date_timezone) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9), (:db_insert_placeholder_10, :db_insert_placeholder_11, :db_insert_placeholder_12, :db_insert_placeholder_13, :db_insert_placeholder_14, :db_insert_placeholder_15, :db_insert_placeholder_16, :db_insert_placeholder_17, :db_insert_placeholder_18, :db_insert_placeholder_19)&#039;, Array, Array) (Line: 358)
Drupal\Core\Database\Driver\mysql\Connection-&gt;query(&#039;INSERT INTO {node__field_event_date} (entity_id, revision_id, bundle, delta, langcode, field_event_date_value, field_event_date_end_value, field_event_date_duration, field_event_date_rrule, field_event_date_timezone) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9), (:db_insert_placeholder_10, :db_insert_placeholder_11, :db_insert_placeholder_12, :db_insert_placeholder_13, :db_insert_placeholder_14, :db_insert_placeholder_15, :db_insert_placeholder_16, :db_insert_placeholder_17, :db_insert_placeholder_18, :db_insert_placeholder_19)&#039;, Array, Array) (Line: 32)
Drupal\Core\Database\Driver\mysql\Insert-&gt;execute() (Line: 1420)
Drupal\Core\Entity\Sql\SqlContentEntityStorage-&gt;saveToDedicatedTables(Object, , Array) (Line: 997)
Drupal\Core\Entity\Sql\SqlContentEntityStorage-&gt;doSaveFieldItems(Object) (Line: 657)
Drupal\Core\Entity\ContentEntityStorageBase-&gt;doSave(NULL, Object) (Line: 457)
Drupal\Core\Entity\EntityStorageBase-&gt;save(Object) (Line: 838)
Drupal\Core\Entity\Sql\SqlContentEntityStorage-&gt;save(Object) (Line: 394)
Drupal\Core\Entity\EntityBase-&gt;save() (Line: 294)
Drupal\node\NodeForm-&gt;save(Array, Object)
call_user_func_array(Array, Array) (Line: 111)
Drupal\Core\Form\FormSubmitter-&gt;executeSubmitHandlers(Array, Object) (Line: 51)
Drupal\Core\Form\FormSubmitter-&gt;doSubmitForm(Array, Object) (Line: 590)
Drupal\Core\Form\FormBuilder-&gt;processForm(&#039;node_event_form&#039;, Array, Object) (Line: 319)
Drupal\Core\Form\FormBuilder-&gt;buildForm(Object, Object) (Line: 61)
Drupal\Core\Entity\EntityFormBuilder-&gt;getForm(Object) (Line: 129)
Drupal\node\Controller\NodeController-&gt;add(Object)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber-&gt;Drupal\Core\EventSubscriber\{closure}() (Line: 582)
Drupal\Core\Render\Renderer-&gt;executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber-&gt;wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber-&gt;Drupal\Core\EventSubscriber\{closure}() (Line: 151)
Symfony\Component\HttpKernel\HttpKernel-&gt;handleRaw(Object, 1) (Line: 68)
Symfony\Component\HttpKernel\HttpKernel-&gt;handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session-&gt;handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle-&gt;handle(Object, 1, 1) (Line: 106)
Drupal\page_cache\StackMiddleware\PageCache-&gt;pass(Object, 1, 1) (Line: 85)
Drupal\page_cache\StackMiddleware\PageCache-&gt;handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware-&gt;handle(Object, 1, 1) (Line: 52)
Drupal\Core\StackMiddleware\NegotiationMiddleware-&gt;handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel-&gt;handle(Object, 1, 1) (Line: 693)
Drupal\Core\DrupalKernel-&gt;handle(Object) (Line: 19)

Since there is no upgrade path from 1.10 to 2.0 alpha I did try to uninstall and reinstall the module with similar errors; along with deleting and recreating the field.

Let me know if you want me to try on simplytest.me or a clean drupal install.

mandclu’s picture

Hmmm I'm surprised that uninstalling and then reinstalling didn't do the trick, it definitely sounds a problem with having old schema. If upgrading a 1.x install you could try using devel_entity_updates. If it's not too much trouble trying a clean install, please try with this patch on the 8.x-2.x-dev version.

mandclu’s picture

StatusFileSize
new13.71 KB
new73.27 KB

OK I did some thinking and testing on the output. When a user has their own timezone set, the dates and times will appear using this. When it's overridden, it will output using that instead.

This updated patch adds to the output in this latter case to also output using the user's timezone (if set, otherwise the site default). An example is below:

mandclu’s picture

@laura.gates I was able to also test it using simplytest.me, if that's easier for you. Note that having the timezone in the output depends on having the timezone token (e.g. e or T) in your smart date format.

laura.gates’s picture

@mandclu

So the updated patch didn't change anything on my work site. So I gave it a test on simplytest.me and it's working fine.

So once there is an update mechanism from 1.x to 2.x, I'll give it another go.

  • mandclu committed 440d5a3 on 8.x-2.x
    Issue #3081258 by Chris Gillis, mandclu, laura.gates: Allow user to...
mandclu’s picture

Status: Needs review » Fixed

Since we're still in alpha (and I wanted to create a new release anyway) I decided to merge this in. I'll try to get an upgrade script into the next release. Please reopen if you find additional issues.

mandclu’s picture

@laura.gates there is now a patch to provide an upgrade path at #3105755: hook_update_N implementation to add necessary columns. If you could test that patch and provide your feedback there it would be appreciated.

Status: Fixed » Closed (fixed)

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