时区列表与时区设置方法

节前做海外项目的时候,需要根据海外用户的归属地,下发不同的控制策略。这时候遇到两个问题。

  1. 如何获取用户归属地的时区信息
  2. 如何设置时区信息

我们先来看下为什么会有时区的概念。

引入时区概念是因为地球绕着地轴自转,导致世界不同地区的日出日落时间不同。 为了以一致和标准化的方式记录时间,地球被划分为 24 个时区,每个时区的宽度约为经度 15 度。

在引入时区之前,每个城市都有自己的本地时间,这是由太阳在天空中的位置决定的。 这导致了一个混乱和不一致的系统,火车和电报在不同的地方使用不同的时间,使得城市之间的交通和通讯难以协调。

引入时区标准化计时,更容易协调交通和通信,减少混乱。 今天,时区是我们全球基础设施的重要组成部分,计算机、移动设备和其他系统使用时区以一致和标准化的方式跟踪时间。

中国幅员辽阔,按国际通行时区划分标准可划分为东五区、东六区、东七区、东八区、东九区5个时区。民国时期全国时区被划分为昆仑时区、回藏时区(后改称新藏时区)、陇蜀时区、中原时区、以及长白时区。1949年中华人民共和国成立后,中国大陆全境统一划为东八区(UTC+8),同时以北京时间作为全国唯一的标准时间。

除了中华人民共和国的大陆地区之外,香港与澳门也因地理位置以东八区做为标准时间,分别称作香港时间[1]与澳门标准时间[2]。1949年中华民国政府退守台湾后,全国时区仅剩中原时区,现今改称为国家标准时间、“台湾时间”或“台北时间”。

美国由于东西跨度非常宽,共有六个标准时区:

  1. 夏威夷标准时间 (HST)
  2. 太平洋标准时间 (PST)
  3. Mountain Standard Time (MST)
  4. Central Standard Time (CST)
  5. Eastern Standard Time (EST)
  6. Atlantic Standard Time (AST)

每个时区都有自己的标准时间,并且在美国的地图上呈现为一个圆形的区域。当地的标准时间是由当地的地理位置决定的,例如,夏威夷标准时间位于夏威夷群岛,而太平洋标准时间位于太平洋沿岸地区。

时区图

1. 如何获取时区列表?

服务器查看时区列表的方法

我们以centos举例。时区信息存放路径为:/usr/share/zoneinfo。其他系统路径信息可以参考golang获取时区列表的方法。

cd /usr/share/zoneinfo
ls -a  #列举所有的时区信息。上海时区存在Asina目录下。

我们根据路径信息就能拼接出时区,以shanghai举例。 shanghai时区文件存在 Asia目录中,我们进入Asia中,从文件中找到上海的文件“Shanghai”,这个时候时区值就是 “Asia/Shanghai” 。
本地的时区信息,存放在 /etc/localtime中,其实这个文件就是一个链接,指向了/usr/share/zoneinfo目录下的一个文件。

我们通过下面的命令,可以完成时区的设置信息。

# Set timezone
rm -rf /etc/localtime
ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

Python中的获取时区列表的方法。

我们可以借助python timezone模块,直接打印所有的时区即可。

import pytz

timezones = pytz.all_timezones
print(timezones)

Java中的获取时区列表的方法

import java.util.TimeZone;

public class TimeZoneList {
  public static void main(String[] args) {
    String[] availableIDs = TimeZone.getAvailableIDs();
    for (String id : availableIDs) {
      System.out.println(id);
    }
  }
}

在此示例中,TimeZone.getAvailableIDs 方法用于获取所有可用时区 ID 的数组,然后使用 for-each 循环打印这些 ID。

Golang中的获取方法

golang的包并没有python,java齐全,时区列表的获取需要借助系统自带的时区信息。

下面的代码从内容看,是读取服务器的目录信息,然后进行打印。通过路径信息可以看出,在windows,手机环境中就会存在问题。

package main

import (
    "fmt"
    "io/ioutil"
    "strings"
)

var zoneDirs = []string{
    // Update path according to your OS
    "/usr/share/zoneinfo/",
    "/usr/share/lib/zoneinfo/",
    "/usr/lib/locale/TZ/",
}

var zoneDir string

func main() {
    for _, zoneDir = range zoneDirs {
        ReadFile("")
    }
}

func ReadFile(path string) {
    files, _ := ioutil.ReadDir(zoneDir + path)
    for _, f := range files {
        if f.Name() != strings.ToUpper(f.Name()[:1]) + f.Name()[1:] {
            continue
        }
        if f.IsDir() {
            ReadFile(path + "/" + f.Name())
        } else {
            fmt.Println((path + "/" + f.Name())[1:])
        }
    }
}

通过查询找到了另外一个版本,感觉这个版本应该是比较全的。其实后来想了想,其实手机应用中,应该很少使用golang写程序吧。

 package main

 import (
     "bytes"
     "fmt"
     "io/ioutil"
     "os"
     "os/exec"
     "regexp/syntax"
     "runtime"
     "strings"
     "time"
     "unicode/utf8"
     // "golang.org/x/sys/windows/registry"
 )

 // zoneDirs adapted from https://golang.org/src/time/zoneinfo_unix.go

 // https://golang.org/doc/install/source#environment
 // list of available GOOS as of 10th Feb 2017
 // android, darwin, dragonfly, freebsd, linux, netbsd, openbsd, plan9, solaris,windows

 var zoneDirs = map[string]string{
     "android":   "/system/usr/share/zoneinfo/",
     "darwin":    "/usr/share/zoneinfo/",
     "dragonfly": "/usr/share/zoneinfo/",
     "freebsd":   "/usr/share/zoneinfo/",
     "linux":     "/usr/share/zoneinfo/",
     "netbsd":    "/usr/share/zoneinfo/",
     "openbsd":   "/usr/share/zoneinfo/",
     // "plan9":"/adm/timezone/", -- no way to test this platform
     "solaris": "/usr/share/lib/zoneinfo/",
     "windows": `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\`,
 }

 var zoneDir string

 var timeZones []string

 // InSlice ... check if an element is inside a slice
 func InSlice(str string, list []string) bool {
     for _, v := range list {
         if v == str {
             return true
         }
     }
     return false
 }

 // ReadTZFile ... read timezone file and append into timeZones slice
 func ReadTZFile(path string) {
     files, _ := ioutil.ReadDir(zoneDir + path)
     for _, f := range files {
         if f.Name() != strings.ToUpper(f.Name()[:1])+f.Name()[1:] {
             continue
         }
         if f.IsDir() {
             ReadTZFile(path + "/" + f.Name())
         } else {
             tz := (path + "/" + f.Name())[1:]
             // check if tz is already in timeZones slice
             // append if not
             if !InSlice(tz, timeZones) { // need a more efficient method...

                 // convert string to rune
                 tzRune, _ := utf8.DecodeRuneInString(tz[:1])

                 if syntax.IsWordChar(tzRune) { // filter out entry that does not start with A-Za-z such as +VERSION
                     timeZones = append(timeZones, tz)
                 }
             }
         }
     }

 }

 func ListTimeZones() {
     if runtime.GOOS == "nacl" || runtime.GOOS == "" {
         fmt.Println("Unsupported platform")
         os.Exit(0)
     }

     // detect OS
     fmt.Println("Time zones available for : ", runtime.GOOS)
     fmt.Println("------------------------")

     fmt.Println("Retrieving time zones from : ", zoneDirs[runtime.GOOS])

     if runtime.GOOS != "windows" {
         for _, zoneDir = range zoneDirs {
             ReadTZFile("")
         }
     } else { // let's handle Windows
         // if you're building this on darwin/linux
         // chances are you will encounter
         // undefined: registry in registry.OpenKey error message
         // uncomment below if compiling on Windows platform

         //k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`, registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE)

         //if err != nil {
         // fmt.Println(err)
         //}
         //defer k.Close()

         //names, err := k.ReadSubKeyNames(-1)
         //if err != nil {
         // fmt.Println(err)
         //}

         //fmt.Println("Number of timezones : ", len(names))
         //for i := 0; i <= len(names)-1; i++ {
         // check if tz is already in timeZones slice
         // append if not
         // if !InSlice(names[i], timeZones) { // need a more efficient method...
         //  timeZones = append(timeZones, names[i])
         // }
         //}

         // UPDATE : Reading from registry is not reliable
         // better to parse output result by "tzutil /g" command
         // REMEMBER : There is no time difference between Coordinated Universal Time and Greenwich Mean Time ....
         cmd := exec.Command("tzutil", "/l")

         data, err := cmd.Output()

         if err != nil {
             panic(err)
         }

         fmt.Println("UTC is the same as GMT")
         fmt.Println("There is no time difference between Coordinated Universal Time and Greenwich Mean Time ....")
         GMTed := bytes.Replace(data, []byte("UTC"), []byte("GMT"), -1)

         fmt.Println(string(GMTed))

     }

     now := time.Now()

     for _, v := range timeZones {

         if runtime.GOOS != "windows" {

             location, err := time.LoadLocation(v)
             if err != nil {
                 fmt.Println(err)
             }

             // extract the GMT
             t := now.In(location)
             t1 := fmt.Sprintf("%s", t.Format(time.RFC822Z))
             tArray := strings.Fields(t1)
             gmtTime := strings.Join(tArray[4:], "")
             hours := gmtTime[0:3]
             minutes := gmtTime[3:]

             gmt := "GMT" + fmt.Sprintf("%s:%s", hours, minutes)
             fmt.Println(gmt + " " + v)

         } else {
             fmt.Println(v)
         }

     }
     fmt.Println("Total timezone ids : ", len(timeZones))
 }

 func main() {

     ListTimeZones()

 }

php中获取时区列表的方法

php不是非常的熟悉,只能写些简单的代码。通过代码分析,获取列表貌似只能参考时区官方文档

github中开源时区列表

github时区列表链接

2. 如何设置时区?

golang设置时区

Go (Golang),可以使用time.FixedZone函数为一个time.Time值设置时区。

package main

import (
    "fmt"
    "time"
)

func main() {
    location, err := time.LoadLocation("Asia/Shanghai")
    if err != nil {
        fmt.Println(err)
        return
    }

    now := time.Now().In(location)
    fmt.Println("上海的时间是:", now)
}

php设置时区

在 PHP 中我们使用 date_default_timezone_set 函数为整个脚本设置时区。

$timezone = new DateTimeZone('Asia/Shanghai');
$date = new DateTime('now', $timezone);

echo '现在的时间是: ' . $date->format('Y-m-d H:i:s');

javascript或者nodejs中设置时区

function get_local_time(timeZone) {
    let date = new Date();
    let formatter = new Intl.DateTimeFormat('zh-CN', {timeZone: timeZone, timeStyle: 'full', dateStyle: 'short'});
    return formatter.format(date)
}

get_local_time("Asia/Shanghai");

我们借助第三方js来实现设置时区的功能。

var moment = require('moment-timezone');

var timezone = 'Asia/Shanghai';
var date = moment.tz(new Date(), timezone);
console.log(date.format());

请注意,需要先安装 moment-timezone 库,然后才能在项目中使用它。 可以通过在终端中运行以下命令来执行此操作:

npm install moment-timezone

moment-timezone官方文档参考链接

java设置时区

在 Java 中可以使用 TimeZone 类设置时区。 可以使用 getTimeZone 方法获取特定时区的 TimeZone 对象。在此示例中,使用 getTimeZone 方法获取“Asia/Shanghai”时区的 TimeZone 对象,然后使用 setDefault 方法将其设置为默认时区。

import java.util.TimeZone;

public class TimeZoneExample {
  public static void main(String[] args) {
    TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
    TimeZone.setDefault(tz);
  }
}

python设置时区

import datetime
import pytz

timezone = pytz.timezone("America/Los_Angeles")
date = datetime.datetime.now(timezone)

print("Time in Los Angeles:", date)

c设置时区

#include 
#include 
#include 

int main() {
  putenv("TZ=America/Los_Angeles");
  tzset();

  time_t rawtime;
  struct tm *timeinfo;

  time(&rawtime);
  timeinfo = localtime(&rawtime);

  printf("The current time in Los Angeles is: %s", asctime(timeinfo));

  return 0;
}

C#设置时区

在 C# 中,您可以使用 System 命名空间中的 TimeZone 类来设置时区。 TimeZone 类提供有关当前时区的信息,以及在不同时区之间转换的方法。

下面是如何在 C# 中设置时区的示例:

using System;

class Program {
  static void Main(string[] args) {
    TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
    Console.WriteLine("The current time in Los Angeles is: " + TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, tz));
  }
}

在此示例中,FindSystemTimeZoneById 方法用于获取表示太平洋时区的 TimeZoneInfo 对象。 然后使用 ConvertTimeFromUtc 方法将当前 UTC 时间转换为太平洋时区的本地时间,并将结果打印到控制台。

您可以通过调用 GetSystemTimeZones 方法找到支持的时区列表,该方法返回所有可用 TimeZoneInfo 对象的列表。 要为特定位置设置时区,您可以使用适当的时区标识符,例如太平洋时区的“Pacific Standard Time”.

C++设置时区

#include 
#include 
#include 

int main() {
  setenv("TZ", "America/Los_Angeles", 1);
  tzset();

  time_t rawtime;
  struct tm *timeinfo;

  time(&rawtime);
  timeinfo = localtime(&rawtime);

  std::cout << "The current time in Los Angeles is: " << asctime(timeinfo);

  return 0;
}

在此示例中,setenv 函数将 TZ 环境变量设置为 America/Los_Angeles,它指定了太平洋时区。 然后调用tzset函数读取TZ环境变量,为程序设置时区信息。

time 函数用于获取当前时间作为 time_t 值,然后将其传递给 localtime 函数,将其转换为包含分解时间信息的 tm 结构,包括本地时区。 asctime 函数用于将细分的时间信息格式化为字符串,然后打印到控制台。

docker中创建容器时设置时区的方法

大部分 Docker 镜像都是基于 Alpine,Ubuntu,Debian,CentOS 等镜像制作而成。这些镜像基本上都采用 UTC 时间,默认时区为零时区。如果创建web服务的时候,没有指定时区信息,后面会导致日志和统计信息等异常。下面是我常用的一种实现方式。

#创建自己的网络
docker network create --subnet=172.18.0.0/16 my-net

# 我们使用 TZ(timezone的缩写)指定时区参数,使用--network指定ip。(如果docker中存在多个容器,服务重启的时候,你大概率会发现容器的ip变了。)--restart always docker启动的时候,自动启动容器 --volume指定空间。
docker run -d  -e TZ="Asia/Shanghai" --network my-net --ip 172.18.0.2  --publish 6379:6379 --restart always --volume /data/redis:/data/redis --volume /data/redis/redis.conf:/etc/redis/redis.conf --name my-redis  redis redis-server /etc/redis/redis.conf

服务器设置时区的方法

使用Asia/Shanghai时区作为服务器的默认时区。

# Set timezone
rm -rf /etc/localtime
ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

3. 参考链接

  1. 维基百科时区列表
  2. 时区列表参考
  3. 一个比较不错的时区列表,包含了主要的城市
  4. dockerfile创建镜像时指定时区
  5. dockerfile set timezone method
  6. 时区列表检索链接
end
  • 作者:kali(作者介绍)
  • 更新时间:2022-07-20 18:09
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 转载声明:转载站点文章,请附上原文链接
  • 翻译声明:翻译文章会不严谨,请务必附上原文链接
  • 扫描阅读:扫描二维码,手机阅读该文章