Skip to main content

Timezone Architecture

WooCommerce Appointments features a comprehensive timezone system that handles time zone conversions, local time display, and scheduling across different time zones. This architecture ensures accurate appointment scheduling regardless of the user's location.

Timezone Components

WordPress Timezone Integration

The system builds on WordPress's native timezone handling:

// Get WordPress configured timezone
$wp_timezone = wp_timezone(); // Returns DateTimeZone object

// Get current time in WordPress timezone
$local_time = new DateTime('now', $wp_timezone);

Appointment Time Storage

All appointments are stored in UTC in the database for consistency:

-- Database storage (always UTC)
appointment_start: 2026-01-20 15:00:00 UTC
appointment_end: 2026-01-20 16:00:00 UTC

Timezone Conversion Methods

Local Time Methods

The system uses WordPress local time functions for consistent display:

// Convert UTC timestamp to local time
$local_start = get_date_from_gmt(gmdate('Y-m-d H:i:s', $utc_timestamp));

// Format local time for display
$formatted_time = date_i18n(get_option('time_format'), $utc_timestamp);

Timezone Detection

Automatic timezone detection for customers and staff:

// Customer timezone detection
$customer_timezone = WC_Appointments_Timezone::detect_customer_timezone();

// Staff timezone detection
$staff_timezone = WC_Appointments_Timezone::get_staff_timezone($staff_id);

Timezone Architecture Flow

1. Appointment Creation

Customer selects time → Convert to UTC → Store in database

2. Display Logic

UTC from database → Convert to user timezone → Display locally

3. Availability Checking

User request time → Convert to UTC → Check availability → Return results

Timezone Classes

WC_Appointments_Timezone

Main timezone handling class:

class WC_Appointments_Timezone {

// Get WordPress timezone as DateTimeZone
public static function get_wp_timezone() {
return wp_timezone();
}

// Convert UTC timestamp to local time
public static function utc_to_local($utc_timestamp, $timezone = null) {
$timezone = $timezone ?: self::get_wp_timezone();
$datetime = new DateTime('@' . $utc_timestamp);
$datetime->setTimezone($timezone);
return $datetime;
}

// Convert local time to UTC timestamp
public static function local_to_utc($local_time, $timezone = null) {
$timezone = $timezone ?: self::get_wp_timezone();
$datetime = new DateTime($local_time, $timezone);
return $datetime->getTimestamp();
}
}

WC_Appointments_Timezone_Detection

Handles automatic timezone detection:

class WC_Appointments_Timezone_Detection {

// Detect customer timezone from browser/IP
public static function detect_customer_timezone() {
// Priority order:
// 1. User profile setting
// 2. Browser JavaScript detection
// 3. IP geolocation
// 4. WordPress default
}

// Get staff member's configured timezone
public static function get_staff_timezone($staff_id) {
return get_user_meta($staff_id, 'timezone', true) ?: wp_timezone();
}
}

Frontend Timezone Handling

JavaScript Timezone Detection

// Browser timezone detection
const browserTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;

// Send to server for appointment calculations
fetch('/wp-json/wc-appointments/v2/slots', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Timezone': browserTimezone
},
body: JSON.stringify({
product_id: 123,
date: '2026-01-20'
})
});

Local Time Display

// Convert UTC timestamps to local time for display
function formatLocalTime(utcTimestamp) {
const date = new Date(utcTimestamp * 1000);
return date.toLocaleString();
}

Backend Timezone Processing

Slot Generation with Timezones

class WC_Appointments_Slot_Generator {

public function generate_slots($product_id, $date_range, $timezone = null) {
$timezone = $timezone ?: WC_Appointments_Timezone::get_wp_timezone();

// Convert date range to UTC for processing
$utc_start = WC_Appointments_Timezone::local_to_utc($date_range['start'], $timezone);
$utc_end = WC_Appointments_Timezone::local_to_utc($date_range['end'], $timezone);

// Generate slots in UTC
$slots = $this->generate_utc_slots($product_id, $utc_start, $utc_end);

// Convert back to requested timezone for display
return $this->convert_slots_to_timezone($slots, $timezone);
}
}

Availability Rule Processing

class WC_Appointments_Availability_Rule {

public function is_available($utc_timestamp, $timezone = null) {
$timezone = $timezone ?: WC_Appointments_Timezone::get_wp_timezone();

// Convert UTC timestamp to rule's timezone for evaluation
$local_time = WC_Appointments_Timezone::utc_to_local($utc_timestamp, $this->timezone);

// Check rule in local time context
return $this->evaluate_rule($local_time);
}
}

Timezone Configuration

WordPress Settings

Configure timezone in WordPress admin: Settings → General → Timezone

Staff Timezone Settings

Staff can set their personal timezone: Users → Profile → Timezone

Product Timezone Settings

Products can have specific timezone requirements:

  • Service Location Timezone: For location-based services
  • Staff Timezone Override: Force staff timezone for display
  • Customer Timezone Detection: Auto-detect customer timezone

Timezone Best Practices

Database Storage

  • Always store UTC: Store all timestamps in UTC
  • Consistent format: Use Unix timestamps or UTC datetime strings
  • Timezone metadata: Store original timezone if needed for display

Display Logic

  • Convert on display: Convert UTC to local time only when displaying
  • User context: Use user's timezone for personalized display
  • Fallback handling: Always have a fallback timezone (WordPress default)

API Responses

// API response with timezone information
$response = [
'start_utc' => 1738035600, // UTC timestamp
'end_utc' => 1738039200, // UTC timestamp
'timezone' => 'America/New_York', // Display timezone
'start_local' => '2026-01-28 10:00:00', // Local time
'end_local' => '2026-01-28 11:00:00' // Local time
];

Troubleshooting Timezone Issues

Common Problems

Incorrect Time Display

Symptoms: Appointments show wrong time for users

Solutions:

  1. Verify WordPress timezone setting
  2. Check user profile timezone settings
  3. Ensure JavaScript timezone detection is working
  4. Validate UTC storage in database

Daylight Saving Time Issues

Symptoms: Appointments shift by 1 hour during DST changes

Solutions:

  1. Use PHP's DateTimeZone with DST handling
  2. Test DST transitions thoroughly
  3. Update timezone database regularly
  4. Use WordPress built-in DST handling

Multi-timezone Scheduling

Symptoms: Confusion when staff and customers in different timezones

Solutions:

  1. Always display timezone labels
  2. Show both local and UTC times for clarity
  3. Implement timezone confirmation during booking
  4. Use timezone conversion tools for verification

Debug Tools

// Debug timezone conversions
function debug_timezone_conversion($utc_timestamp, $target_timezone) {
$utc_time = new DateTime('@' . $utc_timestamp);
$local_time = clone $utc_time;
$local_time->setTimezone(new DateTimeZone($target_timezone));

error_log("UTC: " . $utc_time->format('Y-m-d H:i:s'));
error_log("Local ($target_timezone): " . $local_time->format('Y-m-d H:i:s'));
}

Timezone Migration

Upgrading from Pre-5.0

Older versions stored times in WordPress local time. Migration process:

// Migration script for existing appointments
function migrate_appointment_times() {
$appointments = get_posts([
'post_type' => 'wc_appointment',
'posts_per_page' => -1
]);

foreach ($appointments as $appointment) {
$start_time = get_post_meta($appointment->ID, '_appointment_start', true);
$end_time = get_post_meta($appointment->ID, '_appointment_end', true);

// Convert local time to UTC
$utc_start = strtotime(get_gmt_from_date(date('Y-m-d H:i:s', $start_time)));
$utc_end = strtotime(get_gmt_from_date(date('Y-m-d H:i:s', $end_time)));

// Update with UTC timestamps
update_post_meta($appointment->ID, '_appointment_start', $utc_start);
update_post_meta($appointment->ID, '_appointment_end', $utc_end);
}
}

Performance Considerations

Timezone Conversion Caching

// Cache timezone conversions for performance
$cache_key = "timezone_convert_{$utc_timestamp}_{$timezone}";
$cached_result = wp_cache_get($cache_key, 'wc_appointments_timezones');

if (false === $cached_result) {
$result = WC_Appointments_Timezone::utc_to_local($utc_timestamp, $timezone);
wp_cache_set($cache_key, $result, 'wc_appointments_timezones', 3600);
}

Database Optimization

  • Index UTC timestamps: Ensure proper indexing on UTC time columns
  • Avoid functions in WHERE: Don't use timezone conversion functions in database queries
  • Pre-compute when possible: Cache frequently used timezone conversions