diff --git a/modules/single_datetime_exposed/single_datetime_exposed.module b/modules/single_datetime_exposed/single_datetime_exposed.module
index 59e335c..1ad94a9 100644
--- a/modules/single_datetime_exposed/single_datetime_exposed.module
+++ b/modules/single_datetime_exposed/single_datetime_exposed.module
@@ -5,79 +5,23 @@
  * Main module file single_datetime_exposed.
  */
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\single_datetime_exposed\Hook\SingleDatetimeExposedHooks;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
-use Drupal\single_datetime\AttributeHelper;
 
 /**
  * Implements hook_help().
  */
+#[LegacyHook]
 function single_datetime_exposed_help($route_name, RouteMatchInterface $route_match) {
-  switch ($route_name) {
-    case 'help.page.single_datetime_exposed':
-      $output = '';
-      $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('Date time picker attached to all Views exposed date fields automatically.');
-      return $output;
-  }
+  return \Drupal::service(SingleDatetimeExposedHooks::class)->help($route_name, $route_match);
 }
 
 /**
  * Implements hook_form_BASE_FORM_ID_alter().
  */
+#[LegacyHook]
 function single_datetime_exposed_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
-  /** @var \Drupal\views\ViewExecutable $view */
-  $view = $form_state->getStorage()['view'];
-
-  if ($view->preview === NULL) {
-    $filters = $view->getHandlers('filter');
-
-    // Use default template from helper.
-    $attributes = AttributeHelper::defaultWidget();
-
-    $active = FALSE;
-
-    foreach ($filters as $filter) {
-      // Only take filters which are exposed and date based.
-      // Only handle date based value, no offset exposed dates.
-      if (!empty($filter['exposed']) && ($filter['plugin_id'] === 'date' || $filter['plugin_id'] === 'search_api_date') && $filter['value']['type'] === 'date') {
-        $active = TRUE;
-        $field_name = $filter['id'];
-
-        // Based on selected filters attach attributes on proper place.
-        switch ($filter['operator']) {
-          case 'between':
-          case 'not between':
-            $form[$field_name]['min']['#attributes'] = $attributes;
-            $form[$field_name]['max']['#attributes'] = $attributes;
-
-            // Adjust titles.
-            if (isset($form[$field_name]['min']['#title'])) {
-              $field_title = $form[$field_name]['min']['#title'];
-              $form[$field_name]['min']['#title'] = $field_title . ' ( ' . t('From') . ' )';
-              $form[$field_name]['max']['#title'] = $field_title . ' ( ' . t('To') . ' )';
-            }
-
-            // On between filters when end value is empty, sometimes filters
-            // are not working. So set end value.
-            $input = $form_state->getUserInput();
-            if (!empty($input[$field_name]['min']) && empty($input[$field_name]['max'])) {
-              $timestamp = strtotime($input[$field_name]['min']) + 86400;
-              $input[$field_name]['max'] = date('Y-m-d h:i:s', $timestamp);
-              $form_state->setUserInput($input);
-            }
-            break;
-
-          default:
-            $form[$field_name]['#attributes'] = $attributes;
-        }
-      }
-    }
-
-    // If we have found filters. Attach library.
-    if ($active) {
-      $form['#attached']['library'][] = 'single_datetime/datetimepicker';
-    }
-  }
-
+  \Drupal::service(SingleDatetimeExposedHooks::class)->formViewsExposedFormAlter($form, $form_state, $form_id);
 }
diff --git a/modules/single_datetime_exposed/single_datetime_exposed.services.yml b/modules/single_datetime_exposed/single_datetime_exposed.services.yml
new file mode 100644
index 0000000..fec196d
--- /dev/null
+++ b/modules/single_datetime_exposed/single_datetime_exposed.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\single_datetime_exposed\Hook\SingleDatetimeExposedHooks:
+    class: Drupal\single_datetime_exposed\Hook\SingleDatetimeExposedHooks
+    autowire: true
diff --git a/modules/single_datetime_exposed/src/Hook/SingleDatetimeExposedHooks.php b/modules/single_datetime_exposed/src/Hook/SingleDatetimeExposedHooks.php
new file mode 100644
index 0000000..0db4d00
--- /dev/null
+++ b/modules/single_datetime_exposed/src/Hook/SingleDatetimeExposedHooks.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Drupal\single_datetime_exposed\Hook;
+
+use Drupal\single_datetime\AttributeHelper;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Hook implementations for single_datetime_exposed.
+ */
+class SingleDatetimeExposedHooks {
+  use StringTranslationTrait;
+
+  /**
+   * Implements hook_help().
+   */
+  #[Hook('help')]
+  public function help($route_name, RouteMatchInterface $route_match) {
+    switch ($route_name) {
+      case 'help.page.single_datetime_exposed':
+        $output = '';
+        $output .= '<h3>' . $this->t('About') . '</h3>';
+        $output .= '<p>' . $this->t('Date time picker attached to all Views exposed date fields automatically.');
+        return $output;
+    }
+  }
+
+  /**
+   * Implements hook_form_BASE_FORM_ID_alter().
+   */
+  #[Hook('form_views_exposed_form_alter')]
+  public function formViewsExposedFormAlter(&$form, FormStateInterface $form_state, $form_id) {
+    /** @var \Drupal\views\ViewExecutable $view */
+    $view = $form_state->getStorage()['view'];
+    if ($view->preview === NULL) {
+      $filters = $view->getHandlers('filter');
+      // Use default template from helper.
+      $attributes = AttributeHelper::defaultWidget();
+      $active = FALSE;
+      foreach ($filters as $filter) {
+        // Only take filters which are exposed and date based.
+        // Only handle date based value, no offset exposed dates.
+        if (!empty($filter['exposed']) && ($filter['plugin_id'] === 'date' || $filter['plugin_id'] === 'search_api_date') && $filter['value']['type'] === 'date') {
+          $active = TRUE;
+          $field_name = $filter['id'];
+          // Based on selected filters attach attributes on proper place.
+          switch ($filter['operator']) {
+            case 'between':
+            case 'not between':
+              $form[$field_name]['min']['#attributes'] = $attributes;
+              $form[$field_name]['max']['#attributes'] = $attributes;
+              // Adjust titles.
+              if (isset($form[$field_name]['min']['#title'])) {
+                $field_title = $form[$field_name]['min']['#title'];
+                $form[$field_name]['min']['#title'] = $field_title . ' ( ' . $this->t('From') . ' )';
+                $form[$field_name]['max']['#title'] = $field_title . ' ( ' . $this->t('To') . ' )';
+              }
+              // On between filters when end value is empty, sometimes filters
+              // are not working. So set end value.
+              $input = $form_state->getUserInput();
+              if (!empty($input[$field_name]['min']) && empty($input[$field_name]['max'])) {
+                $timestamp = strtotime($input[$field_name]['min']) + 86400;
+                $input[$field_name]['max'] = date('Y-m-d h:i:s', $timestamp);
+                $form_state->setUserInput($input);
+              }
+              break;
+
+            default:
+              $form[$field_name]['#attributes'] = $attributes;
+          }
+        }
+      }
+      // If we have found filters. Attach library.
+      if ($active) {
+        $form['#attached']['library'][] = 'single_datetime/datetimepicker';
+      }
+    }
+  }
+
+}
diff --git a/single_datetime.module b/single_datetime.module
index fbdce23..7d8c2c7 100644
--- a/single_datetime.module
+++ b/single_datetime.module
@@ -5,24 +5,14 @@
  * Main module file single_datetime.
  */
 
+use Drupal\Core\Hook\Attribute\LegacyHook;
+use Drupal\single_datetime\Hook\SingleDatetimeHooks;
 use Drupal\Core\Routing\RouteMatchInterface;
 
 /**
  * Implements hook_help().
  */
+#[LegacyHook]
 function single_datetime_help($route_name, RouteMatchInterface $route_match) {
-  switch ($route_name) {
-    case 'help.page.single_datetime':
-      $output = '';
-      $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('Date time picker form element for date and date range fields using the xdan jQuery Plugin Date and Time Picker library');
-      $output .= '<h3>' . t('Install jQuery Plugin Date and Time Picker library') . '</h3>';
-      $output .= '<p>' . t('It is possible to place library manual or with composer.') . '</p>';
-      $output .= '<p>' . t('See <a href=":readme">README.md</a> inside this module or go to project page for detailed instructions',
-          [':readme' => 'https://git.drupalcode.org/project/single_datetime/-/blob/@/README.md']) . '</p>';
-      $output .= '<h3>' . t('External resources') . '</h3>';
-      $output .= '<p><a href="https://xdsoft.net/jqplugins/datetimepicker/">DateTimePicker</a></p>';
-      $output .= '<p><a href="https://github.com/xdan/datetimepicker">Github</a></p>';
-      return $output;
-  }
+  return \Drupal::service(SingleDatetimeHooks::class)->help($route_name, $route_match);
 }
diff --git a/single_datetime.services.yml b/single_datetime.services.yml
new file mode 100644
index 0000000..b9d2ad2
--- /dev/null
+++ b/single_datetime.services.yml
@@ -0,0 +1,5 @@
+
+services:
+  Drupal\single_datetime\Hook\SingleDatetimeHooks:
+    class: Drupal\single_datetime\Hook\SingleDatetimeHooks
+    autowire: true
diff --git a/src/Hook/SingleDatetimeHooks.php b/src/Hook/SingleDatetimeHooks.php
new file mode 100644
index 0000000..f9d83e8
--- /dev/null
+++ b/src/Hook/SingleDatetimeHooks.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Drupal\single_datetime\Hook;
+
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Hook\Attribute\Hook;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+
+/**
+ * Hook implementations for single_datetime.
+ */
+class SingleDatetimeHooks {
+  use StringTranslationTrait;
+
+  /**
+   * Implements hook_help().
+   */
+  #[Hook('help')]
+  public function help($route_name, RouteMatchInterface $route_match) {
+    switch ($route_name) {
+      case 'help.page.single_datetime':
+        $output = '';
+        $output .= '<h3>' . $this->t('About') . '</h3>';
+        $output .= '<p>' . $this->t('Date time picker form element for date and date range fields using the xdan jQuery Plugin Date and Time Picker library');
+        $output .= '<h3>' . $this->t('Install jQuery Plugin Date and Time Picker library') . '</h3>';
+        $output .= '<p>' . $this->t('It is possible to place library manual or with composer.') . '</p>';
+        $output .= '<p>' . $this->t('See <a href=":readme">README.md</a> inside this module or go to project page for detailed instructions', [
+          ':readme' => 'https://git.drupalcode.org/project/single_datetime/-/blob/@/README.md',
+        ]) . '</p>';
+        $output .= '<h3>' . $this->t('External resources') . '</h3>';
+        $output .= '<p><a href="https://xdsoft.net/jqplugins/datetimepicker/">DateTimePicker</a></p>';
+        $output .= '<p><a href="https://github.com/xdan/datetimepicker">Github</a></p>';
+        return $output;
+    }
+  }
+
+}
