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:
- Verify WordPress timezone setting
- Check user profile timezone settings
- Ensure JavaScript timezone detection is working
- Validate UTC storage in database
Daylight Saving Time Issues
Symptoms: Appointments shift by 1 hour during DST changes
Solutions:
- Use PHP's DateTimeZone with DST handling
- Test DST transitions thoroughly
- Update timezone database regularly
- Use WordPress built-in DST handling
Multi-timezone Scheduling
Symptoms: Confusion when staff and customers in different timezones
Solutions:
- Always display timezone labels
- Show both local and UTC times for clarity
- Implement timezone confirmation during booking
- 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
Related Documentation
- REST API Timezone Handling - API timezone parameters
- Calendar Timezone Display - Calendar timezone features