You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP 8.1+Laravel 9:如何通过国家和地区查询时区?

嘿,这个问题我之前帮不少Laravel开发者解决过——确实,旧的GeoIP扩展停更后,想用国家/地区代码直接查时区,而GeoIP2又只支持IP查询,确实有点卡壳。不过针对你的Laravel 9 + PHP 8.1环境,有几个靠谱的解决方案,你可以根据自己的业务场景选:

方案1:基于MaxMind GeoIP2离线数据库自定义查询

MaxMind的GeoIP2数据库(免费版也可用)其实包含了时区信息,我们可以绕开“只能通过IP查询”的限制,自己实现一套通过国家/地区代码查询时区的逻辑。

步骤:

  1. 安装依赖:通过Composer安装MaxMind的官方包:
    composer require maxmind-db/reader geoip2/geoip2
    
  2. 下载离线数据库:去MaxMind官网免费注册后,下载GeoIP2-City.mmdb(免费版的GeoIP2-Country只有国家级时区,城市版能覆盖到地区级),把它放到Laravel的storage/app/geoip/目录下(如果目录不存在就新建)。
  3. 编写查询服务:在Laravel里创建一个服务类,读取数据库并匹配国家/地区代码。这里有个小技巧:第一次运行时可以把所有国家/地区-时区的映射提取出来存到Redis或者数据库表中,后续查询直接读缓存,避免每次遍历数据库的性能损耗。

示例服务类代码:

<?php

namespace App\Services;

use MaxMind\Db\Reader;
use Illuminate\Support\Facades\Cache;

class TimezoneLookupService
{
    protected $reader;
    protected $cacheKey = 'geoip_timezone_map';

    public function __construct()
    {
        $this->reader = new Reader(storage_path('app/geoip/GeoIP2-City.mmdb'));
    }

    public function getTimezoneByCountryAndRegion(string $countryCode, string $regionCode = null): ?string
    {
        // 先尝试从缓存读取映射表
        $timezoneMap = Cache::get($this->cacheKey);
        
        if (!$timezoneMap) {
            // 缓存不存在,构建映射表
            $timezoneMap = [];
            $records = $this->reader->getIterator();
            
            foreach ($records as $record) {
                $countryCode = $record['country']['iso_code'] ?? null;
                $regionCode = $record['subdivisions'][0]['iso_code'] ?? null;
                $timezone = $record['location']['time_zone'] ?? null;
                
                if ($countryCode && $timezone) {
                    $key = $regionCode ? "{$countryCode}-{$regionCode}" : $countryCode;
                    $timezoneMap[strtoupper($key)] = $timezone;
                }
            }
            
            // 缓存映射表,有效期设为30天(MaxMind数据库一般每月更新一次)
            Cache::put($this->cacheKey, $timezoneMap, now()->addDays(30));
        }

        // 构造查询键
        $queryKey = $regionCode ? strtoupper("{$countryCode}-{$regionCode}") : strtoupper($countryCode);
        return $timezoneMap[$queryKey] ?? null;
    }
}

之后你可以在Laravel的服务容器中注册这个服务,或者直接实例化调用,非常方便。

方案2:维护预定义的时区映射数组

如果你的业务只需要覆盖少数常用国家/地区,那直接维护一个硬编码的映射数组是最简单的方案,没有任何依赖,速度也最快。

示例代码:

<?php

// 可以把这个数组放到配置文件里,比如config/timezones.php
return [
    'country_region_map' => [
        'US-CA' => 'America/Los_Angeles',
        'US-NY' => 'America/New_York',
        'GB-ENG' => 'Europe/London',
        'CN-BJ' => 'Asia/Shanghai',
        // 按需添加更多映射
    ],
];

// 查询函数可以封装成辅助函数或者服务
function getTimezoneFromMap(string $countryCode, ?string $regionCode = null): ?string
{
    $map = config('timezones.country_region_map');
    $key = $regionCode ? strtoupper("{$countryCode}-{$regionCode}") : strtoupper($countryCode);
    return $map[$key] ?? null;
}

缺点是需要手动维护映射,当有新的地区或者时区规则变更时,你得自己更新数组,但胜在简单直接。

方案3:调用第三方时区API

如果不想维护本地数据,可以用第三方API来查询,比如TimeZoneDB的API,它支持通过国家代码和地区代码返回时区。不过要注意免费版有请求次数限制,而且依赖网络请求,存在一定延迟。

示例代码(用Laravel的Http客户端):

<?php

use Illuminate\Support\Facades\Http;

function getTimezoneViaApi(string $countryCode, ?string $regionCode = null): ?string
{
    $response = Http::get('http://api.timezonedb.com/v2.1/get-time-zone', [
        'key' => env('TIMEZONE_DB_API_KEY'), // 把API密钥存在.env里
        'country' => strtoupper($countryCode),
        'region' => $regionCode ? strtoupper($regionCode) : '',
        'format' => 'json',
    ]);

    if ($response->successful() && $response->json('status') === 'OK') {
        return $response->json('zoneName');
    }

    return null;
}

这个方案适合查询频率不高,或者对实时性要求不高的场景。

额外小贴士

不管用哪个方案拿到时区后,你可以直接结合Laravel的Carbon来处理日期时间:

$timezone = app(TimezoneLookupService::class)->getTimezoneByCountryAndRegion('US', 'CA');
$localTime = \Carbon\Carbon::now($timezone);

内容的提问来源于stack exchange,提问作者alireza alizade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 16:15:57