Skip to main content

Timezone Mapping

The PhoneNumberToTimeZonesMapper provides timezone information for phone numbers. This is useful for determining appropriate times to call users or schedule communications.
A single phone number may map to multiple timezones, especially for toll-free and mobile numbers.

Getting Started

1

Get Mapper Instance

use libphonenumber\PhoneNumberToTimeZonesMapper;

$timezoneMapper = PhoneNumberToTimeZonesMapper::getInstance();
2

Parse a Phone Number

$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
$number = $phoneUtil->parse('798765432', 'CH');
3

Get Timezones

// Returns array of timezone identifiers
$timezones = $timezoneMapper->getTimeZonesForNumber($number);
var_dump($timezones); // array(1) { [0]=> string(13) "Europe/Zurich" }

Basic Usage

use libphonenumber\PhoneNumberUtil;
use libphonenumber\PhoneNumberToTimeZonesMapper;

$phoneUtil = PhoneNumberUtil::getInstance();
$timezoneMapper = PhoneNumberToTimeZonesMapper::getInstance();

// Parse a Swiss phone number
$swissNumber = $phoneUtil->parse('798765432', 'CH');

// Get timezone(s)
$timezones = $timezoneMapper->getTimeZonesForNumber($swissNumber);
print_r($timezones);
// Output: Array ( [0] => Europe/Zurich )

Understanding Timezone Results

The method returns an array of IANA timezone identifiers:
$phoneUtil = PhoneNumberUtil::getInstance();
$timezoneMapper = PhoneNumberToTimeZonesMapper::getInstance();

// Geographic number - usually one timezone
$geoNumber = $phoneUtil->parse('044 668 18 00', 'CH');
$timezones = $timezoneMapper->getTimeZonesForNumber($geoNumber);
var_dump($timezones);
// array(1) { [0]=> string(13) "Europe/Zurich" }

// Toll-free number - may span multiple timezones
$tollFree = $phoneUtil->parse('1-800-FLOWERS', 'US');
$timezones = $timezoneMapper->getTimeZonesForNumber($tollFree);
var_dump(count($timezones));
// Could be 35+ timezones (all US zones)
Timezone identifier format: Results use IANA timezone database names (e.g., America/New_York, Europe/London). These are compatible with PHP’s DateTimeZone class.

Working with PHP DateTimeZone

Timezone identifiers can be used directly with PHP’s timezone functions:
$phoneUtil = PhoneNumberUtil::getInstance();
$timezoneMapper = PhoneNumberToTimeZonesMapper::getInstance();

$number = $phoneUtil->parse('+41 44 668 18 00', null);
$timezones = $timezoneMapper->getTimeZonesForNumber($number);

if (!empty($timezones)) {
    // Get the first (or only) timezone
    $timezone = new DateTimeZone($timezones[0]);
    
    // Get current time in that timezone
    $datetime = new DateTime('now', $timezone);
    
    echo "Current time in {$timezones[0]}: ";
    echo $datetime->format('Y-m-d H:i:s T');
    // Output: Current time in Europe/Zurich: 2024-03-10 15:30:00 CET
}

Geographic vs Non-Geographic Numbers

Geographic numbers (landlines) typically map to a single timezone:
$phoneUtil = PhoneNumberUtil::getInstance();
$timezoneMapper = PhoneNumberToTimeZonesMapper::getInstance();

$landline = $phoneUtil->parse('0117 496 0123', 'GB');
$timezones = $timezoneMapper->getTimeZonesForNumber($landline);

print_r($timezones);
// Array ( [0] => Europe/London )

Handling Multiple Timezones

When a number maps to multiple timezones:
function getBestTimeToCall(
    array $timezones,
    int $preferredHour = 10
): ?DateTime {
    if (empty($timezones)) {
        return null;
    }
    
    // Use the first timezone (usually the most common/representative)
    $timezone = new DateTimeZone($timezones[0]);
    
    // Create datetime for preferred hour in that timezone
    $datetime = new DateTime('now', $timezone);
    $datetime->setTime($preferredHour, 0, 0);
    
    // If preferred time has passed today, schedule for tomorrow
    $now = new DateTime('now', $timezone);
    if ($datetime < $now) {
        $datetime->modify('+1 day');
    }
    
    return $datetime;
}

// Usage
$phoneUtil = PhoneNumberUtil::getInstance();
$timezoneMapper = PhoneNumberToTimeZonesMapper::getInstance();

$number = $phoneUtil->parse('+41 44 668 18 00', null);
$timezones = $timezoneMapper->getTimeZonesForNumber($number);

$callTime = getBestTimeToCall($timezones, 10); // 10 AM
if ($callTime) {
    echo "Best time to call: " . $callTime->format('Y-m-d H:i:s T');
}

Geographical Numbers Only

For geographic numbers specifically:
$phoneUtil = PhoneNumberUtil::getInstance();
$timezoneMapper = PhoneNumberToTimeZonesMapper::getInstance();

$number = $phoneUtil->parse('0117 496 0123', 'GB');

// Check if number is geographical
if ($phoneUtil->isNumberGeographical($number)) {
    // More precise timezone data for landlines
    $timezones = $timezoneMapper->getTimeZonesForGeographicalNumber($number);
    echo "Geographic timezone: " . $timezones[0];
}

Practical Examples

class CallScheduler
{
    private PhoneNumberUtil $phoneUtil;
    private PhoneNumberToTimeZonesMapper $timezoneMapper;
    
    public function __construct()
    {
        $this->phoneUtil = PhoneNumberUtil::getInstance();
        $this->timezoneMapper = PhoneNumberToTimeZonesMapper::getInstance();
    }
    
    public function canCallNow(
        string $phoneNumber,
        int $startHour = 9,
        int $endHour = 17
    ): bool {
        try {
            $number = $this->phoneUtil->parse($phoneNumber, null);
            $timezones = $this->timezoneMapper->getTimeZonesForNumber($number);
            
            if (empty($timezones)) {
                return false; // Unknown timezone, don't call
            }
            
            // Check if current time is within business hours
            // in any of the possible timezones
            foreach ($timezones as $tzName) {
                $timezone = new \DateTimeZone($tzName);
                $now = new \DateTime('now', $timezone);
                $currentHour = (int)$now->format('G');
                
                if ($currentHour >= $startHour && $currentHour < $endHour) {
                    return true;
                }
            }
            
            return false;
            
        } catch (\Exception $e) {
            return false;
        }
    }
    
    public function getNextCallWindow(
        string $phoneNumber,
        int $startHour = 9,
        int $endHour = 17
    ): ?DateTime {
        try {
            $number = $this->phoneUtil->parse($phoneNumber, null);
            $timezones = $this->timezoneMapper->getTimeZonesForNumber($number);
            
            if (empty($timezones)) {
                return null;
            }
            
            // Use first timezone
            $timezone = new \DateTimeZone($timezones[0]);
            $now = new \DateTime('now', $timezone);
            $next = clone $now;
            $next->setTime($startHour, 0, 0);
            
            // If start time has passed, schedule for tomorrow
            if ($next <= $now) {
                $next->modify('+1 day');
            }
            
            return $next;
            
        } catch (\Exception $e) {
            return null;
        }
    }
}

// Usage
$scheduler = new CallScheduler();

if ($scheduler->canCallNow('+41 44 668 18 00', 9, 18)) {
    echo "Can call now!";
} else {
    $nextWindow = $scheduler->getNextCallWindow('+41 44 668 18 00', 9, 18);
    if ($nextWindow) {
        echo "Next call window: " . $nextWindow->format('Y-m-d H:i:s T');
    }
}

Handling Unknown Timezones

Some numbers may not have timezone data:
$phoneUtil = PhoneNumberUtil::getInstance();
$timezoneMapper = PhoneNumberToTimeZonesMapper::getInstance();

$number = $phoneUtil->parse($phoneNumber, $region);
$timezones = $timezoneMapper->getTimeZonesForNumber($number);

if (empty($timezones)) {
    // No timezone data - could be:
    // - Invalid number
    // - Region without timezone mapping
    // - Special number type (emergency, etc.)
    
    // Get unknown timezone constant
    $unknown = $timezoneMapper->getUnknownTimeZone();
    echo "Timezone: $unknown"; // "Etc/Unknown"
}

Best Practices

Use First Timezone

When multiple timezones exist, use the first one as the most representative

Respect Business Hours

Always check local time before calling or sending messages

Handle Empty Results

Not all numbers have timezone data - always check for empty arrays

Cache Timezone Lookups

Cache timezone results for frequently accessed numbers

Limitations

What timezone mapping cannot do:
  • Provide real-time location of mobile phones
  • Account for daylight saving time changes automatically (use DateTimeZone for DST)
  • Guarantee single timezone for mobile or toll-free numbers
  • Work with invalid or unparseable numbers

Next Steps

Geocoding

Get geographic location information

Carrier Mapping

Identify mobile carriers

Timezone Mapper API

Complete API reference for timezone mapping

Number Types

Understanding different phone number types

Build docs developers (and LLMs) love