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
Appointment start/end values are stored as Unix timestamps in appointment meta and converted for display/rendering through plugin timezone helpers and rendering paths.
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 Resolution
Timezone is resolved from multiple sources depending on context:
- Frontend booking/day-slot AJAX (
wc_appointments_find_scheduled_day_slots) uses posted form timezone fields first, thendisplay_timezone, then cookie fallback. - Staff/admin views use
WC_Appointments_Timezone_Helpermethods (for exampleget_admin_user_timezone()andget_staff_timezone()).
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_Helper
Main timezone helper class (introduced in 5.2.0):
class WC_Appointments_Timezone_Helper {
// Get site timezone string (e.g., 'America/New_York')
public static function get_site_timezone_string(): string;
// Get user's effective timezone (falls back to site timezone if using default)
public static function get_user_timezone( int $user_id = 0 ): string;
// Get user's raw stored timezone (empty string = using default)
public static function get_user_timezone_raw( int $user_id = 0 ): string;
// Check if user is using the default (site) timezone
public static function is_using_default_timezone( int $user_id = 0 ): bool;
// Save user timezone preference (empty string = use default)
public static function save_user_timezone( int $user_id, string $timezone ): bool;
// Get staff member's timezone
public static function get_staff_timezone( int $staff_id ): string;
// Get timezone indicator for admin display
public static function get_timezone_indicator( string $timezone ): string;
}
Default Timezone Handling
The helper class treats empty string as "use default (site) timezone":
// Save user preference - empty = default
WC_Appointments_Timezone_Helper::save_user_timezone( $user_id, '' ); // Uses site TZ
// Check if using default
if ( WC_Appointments_Timezone_Helper::is_using_default_timezone( $user_id ) ) {
// User will follow site timezone changes automatically
}
// Get raw value to check explicit preference
$raw_tz = WC_Appointments_Timezone_Helper::get_user_timezone_raw( $user_id );
if ( empty( $raw_tz ) ) {
// User hasn't set explicit preference
}
Timezone Selection Function
The wc_appointments_wp_timezone_choice() function generates timezone dropdowns:
// Generate timezone select options with Default option
$options = wc_appointments_wp_timezone_choice( $selected_value );
// Without Default option (for specific timezone requirement)
$options = wc_appointments_wp_timezone_choice( $selected_value, '', false );
The Default option:
- Has an empty value (
"") - Displays as
Default (City Name)with current site timezone - Is automatically selected when no value is stored
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
Timezone-aware slot generation and rendering in current plugin versions is handled through these real paths:
WC_Appointments_WC_Ajax::find_scheduled_day_slots()for frontend day/month data.WC_Appointment_Slots_Provider::get_collection()as the slots source of truth.WC_Appointments_Controller::render_time_slots_html()for frontend slot HTML rendering.WC_Appointments_Timezone_Helperfor user/site/admin timezone decisions and conversion.
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
Disabling timezone display
For single-timezone or local-only sites, you can hide all timezone UI (pickers, columns, labels, abbreviations) using the woocommerce_appointments_show_timezone filter. When the filter returns false for a given context and user, the plugin uses the site timezone only and does not show timezone selectors or abbreviations anywhere (frontend, admin, emails).
This filter affects only display. Google Calendar sync always uses the site timezone when pushing events; it does not check this filter. If the connected Google Calendar is set to a different timezone, events will sync to the correct moment in time but may show a different clock time in Google. For predictable behaviour when timezones are disabled, keep the Google Calendar timezone aligned with the site. See Hooks – woocommerce_appointments_show_timezone for full notes and other edge cases.
// Disable timezone display everywhere (local-only site)
add_filter( 'woocommerce_appointments_show_timezone', '__return_false', 10, 3 );
// Disable only on frontend (admin still sees timezones)
add_filter( 'woocommerce_appointments_show_timezone', function( $show, $context ) {
return $context !== 'frontend';
}, 10, 2 );
// Disable only in admin
add_filter( 'woocommerce_appointments_show_timezone', function( $show, $context ) {
return $context !== 'admin';
}, 10, 2 );
// Disable for a specific user ID
add_filter( 'woocommerce_appointments_show_timezone', function( $show, $context, $user_id ) {
return $user_id !== 42;
}, 10, 3 );
Filter parameters: $show (bool), $context ('frontend' or 'admin'), $user_id (int; 0 for guests). Return true to show timezone, false to hide. Full reference and more examples: Hooks & Filters – woocommerce_appointments_show_timezone.
Related Documentation
- Hooks & Filters – woocommerce_appointments_show_timezone - Disable timezone display
- Setup: Timezones - Store, customer, and staff timezone configuration
- REST API Timezone Handling - API timezone parameters
- Calendar Timezone Display - Calendar timezone features