时区与程序设计

时区的定义

我们使用经纬度[1]来标识地球上的任意一个点。

理论时区

不像纬度有赤道作为自然的起点,经度没有自然的起点而使用经过伦敦格林尼治天文台旧址的子午线作为起点。

理论时区的宽度是15°,所以一共有 360 / 15 = 24 个时区,一天有 24 小时,所以每个时区正好对应一个小时。自子午线向东,这些时区的名称为:中时区(以子午线为中心的时区)、东一区、东二区...东十二区、西十一区、西十区...西一区[2]。

31678

由于地球的自转方向为自西向东,所以越东的时区时间越早。

实际时区

为了避开国界线,有的时区的形状并不规则,而且比较大的国家以国家内部行政分界线为时区界线,这是实际时区,即法定时区。[2]

同一国家可以有不同的时区,同一国家也可以是同一个时区。

  • 比如美国的夏威夷州是 UTC-10,而加利福尼亚州是 UTC-8
  • 整个中国的理论时区横跨了从东五区(UTC+5)到东九区(UTC+9)共计五个时区,但国家只有一个时区:北京时间

时区会变化

Why is subtracting these two times (in 1927) giving a strange result?

时区的 offset 不正确[4]

有人推测是时区数据库错了而不是 pytz 的问题。但我在其他编程语言的时区库中没有搜索到相关问题。

import datetime
import pytz
shanghai_tz = pytz.timezone('Asia/Shanghai')
# 在初始化中传入的时区的 offset 是不准确的
>>> datetime.datetime(2018, 1, 1, tzinfo=shanghai_tz)
datetime.datetime(2018, 1, 1, 0, 0, tzinfo=<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>)# 要使用 pytz 文档中的 localize 才准确
>>> shanghai_tz.localize(datetime.datetime(2018, 1, 1))
datetime.datetime(2018, 1, 1, 0, 0, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)

pytz.tzinfo.localize 的源码复杂:

def localize(self, dt, is_dst=False):'''Convert naive time to local time.This method should be used to construct localtimes, ratherthan passing a tzinfo argument to a datetime constructor.is_dst is used to determine the correct timezone in the ambigousperiod at the end of daylight saving time.>>> from pytz import timezone>>> fmt = '%Y-%m-%d %H:%M:%S %Z (%z)'>>> amdam = timezone('Europe/Amsterdam')>>> dt  = datetime(2004, 10, 31, 2, 0, 0)>>> loc_dt1 = amdam.localize(dt, is_dst=True)>>> loc_dt2 = amdam.localize(dt, is_dst=False)>>> loc_dt1.strftime(fmt)'2004-10-31 02:00:00 CEST (+0200)'>>> loc_dt2.strftime(fmt)'2004-10-31 02:00:00 CET (+0100)'>>> str(loc_dt2 - loc_dt1)'1:00:00'Use is_dst=None to raise an AmbiguousTimeError for ambiguoustimes at the end of daylight saving time>>> try:...     loc_dt1 = amdam.localize(dt, is_dst=None)... except AmbiguousTimeError:...     print('Ambiguous')Ambiguousis_dst defaults to False>>> amdam.localize(dt) == amdam.localize(dt, False)Trueis_dst is also used to determine the correct timezone in thewallclock times jumped over at the start of daylight saving time.>>> pacific = timezone('US/Pacific')>>> dt = datetime(2008, 3, 9, 2, 0, 0)>>> ploc_dt1 = pacific.localize(dt, is_dst=True)>>> ploc_dt2 = pacific.localize(dt, is_dst=False)>>> ploc_dt1.strftime(fmt)'2008-03-09 02:00:00 PDT (-0700)'>>> ploc_dt2.strftime(fmt)'2008-03-09 02:00:00 PST (-0800)'>>> str(ploc_dt2 - ploc_dt1)'1:00:00'Use is_dst=None to raise a NonExistentTimeError for these skippedtimes.>>> try:...     loc_dt1 = pacific.localize(dt, is_dst=None)... except NonExistentTimeError:...     print('Non-existent')Non-existent'''if dt.tzinfo is not None:raise ValueError('Not naive datetime (tzinfo is already set)')# Find the two best possibilities.possible_loc_dt = set()for delta in [timedelta(days=-1), timedelta(days=1)]:loc_dt = dt + deltaidx = max(0, bisect_right(self._utc_transition_times, loc_dt) - 1)inf = self._transition_info[idx]tzinfo = self._tzinfos[inf]loc_dt = tzinfo.normalize(dt.replace(tzinfo=tzinfo))if loc_dt.replace(tzinfo=None) == dt:possible_loc_dt.add(loc_dt)if len(possible_loc_dt) == 1:return possible_loc_dt.pop()# If there are no possibly correct timezones, we are attempting# to convert a time that never happened - the time period jumped# during the start-of-DST transition period.if len(possible_loc_dt) == 0:# If we refuse to guess, raise an exception.if is_dst is None:raise NonExistentTimeError(dt)# If we are forcing the pre-DST side of the DST transition, we# obtain the correct timezone by winding the clock forward a few# hours.elif is_dst:return self.localize(dt + timedelta(hours=6), is_dst=True) - timedelta(hours=6)# If we are forcing the post-DST side of the DST transition, we# obtain the correct timezone by winding the clock back.else:return self.localize(dt - timedelta(hours=6),is_dst=False) + timedelta(hours=6)# If we get this far, we have multiple possible timezones - this# is an ambiguous case occuring during the end-of-DST transition.# If told to be strict, raise an exception since we have an# ambiguous caseif is_dst is None:raise AmbiguousTimeError(dt)# Filter out the possiblilities that don't match the requested# is_dstfiltered_possible_loc_dt = [p for p in possible_loc_dt if bool(p.tzinfo._dst) == is_dst]# Hopefully we only have one possibility left. Return it.if len(filtered_possible_loc_dt) == 1:return filtered_possible_loc_dt[0]if len(filtered_possible_loc_dt) == 0:filtered_possible_loc_dt = list(possible_loc_dt)# If we get this far, we have in a wierd timezone transition# where the clocks have been wound back but is_dst is the same# in both (eg. Europe/Warsaw 1915 when they switched to CET).# At this point, we just have to guess unless we allow more# hints to be passed in (such as the UTC offset or abbreviation),# but that is just getting silly.## Choose the earliest (by UTC) applicable timezone if is_dst=True# Choose the latest (by UTC) applicable timezone if is_dst=False# i.e., behave like end-of-DST transitiondates = {}  # utc -> localfor local_dt in filtered_possible_loc_dt:utc_time = (local_dt.replace(tzinfo=None) - local_dt.tzinfo._utcoffset)assert utc_time not in datesdates[utc_time] = local_dtreturn dates[[min, max][not is_dst](dates)]

总结一下,对于 pytz,获取带时区的时间要使用 tz.localize(),将一个转换为另一个时区要 dt_with_tz.astimezone(another_tz)[5]。

程序设计

由于时区的最小单位是小时:

  • 所以如果要区分时区,那么储存的时间必须包含小时,比如你不能只储存到天2018-01-01
  • 所以储存的时间也要包含时区,比如 MongoDB 储存的时区为 UTC
    • The official BSON specification refers to the BSON Date type as the UTC datetime.[3]

程序中的时区不应该与机器所在的时区挂钩,否则,假如从中国机房迁移到美国机房,那么你的程序就会出问题。

只需要一个时区

比如对于大部分中国的程序来说,只需要考虑北京时间这一个时区。这里称这个时区为当地时区。

我认为在程序中(前端、后端)可以只使用当地时区。好处有:

  • 增强可读性,减少混乱。比如调试时看北京时间肯定比 UTC 时间更直观
  • 避免不必要的时区转换

如果数据库的时区可以修改,那么也修改为当地时区,否则,使用数据库的时区。

比如 MongoDB 使用 UTC 时区储存,不可更改(我没有搜索到更改的配置),那么如果有按月分表,那么也使用 UTC 划分月,这样数据库的时区就统一为了 UTC;如果使用当地时区分月,那么就会造成分歧。

需要多个时区

在程序内部使用 UTC 时区,展示数据时使用用户选择的时区。

参考

  1. https://zh.wikipedia.org/wiki/经纬度
  2. https://zh.wikipedia.org/wiki/时区
  3. https://docs.mongodb.com/manual/reference/bson-types/#document-bson-type-date
  4. https://bugs.launchpad.net/pytz/+bug/1678858
  5. http://pytz.sourceforge.net/#localized-times-and-date-arithmetic

转载于:https://www.cnblogs.com/jay54520/p/9431333.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/570570.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JSP实例-彩色验证码

image.java用于产生彩色验证码&#xff0c;其代码如下&#xff1a; package test; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; import java.ut…

P1678 烦恼的高考志愿

题目背景 计算机竞赛小组的神牛V神终于结束了万恶的高考&#xff0c;然而作为班长的他还不能闲下来&#xff0c;班主任老t给了他一个艰巨的任务&#xff1a;帮同学找出最合理的大学填报方案。可是v神太忙了&#xff0c;身后还有一群小姑娘等着和他约会&#xff0c;于是他想到了…

Java代码实现Fibonacci数列

Fibonacci数列 1.定义&#xff1a; 0, 1, 1, 2, 3, 5, 8, 13, 21, . . .; f0 0, f1 1, fn fn−1 fn−2 for n ≥ 2. 2.两种实现算法&#xff1a; 2.1递归&#xff08;recursive&#xff09; T(n)>2n/2证明: T(n)T(n−1)T(n−2)>2T(n−2)>22T(n−4)>...>2n/2T…

套路:想戒手机?试试把屏幕变灰

简评&#xff1a;园长试过了&#xff0c;瞬间不想再看手机&#xff0c;一股浓浓的性冷淡&#xff08;无能&#xff09;风&#xff0c;此时我只想去拥抱真实的世界。 我代表原作者。 我的手机已经变灰了&#xff0c;感觉很不错&#xff0c;不再沉迷。 为了戒掉手机瘾&#xff0c…

判断一个数是否存在于一个非递减的有序数列中 算法(Ordered Search Problem)

1. Description Given a list of nnumbers in non-decreasing order A{a1,a2,⋯,an}such that a1≤a2≤⋯≤anand a number x, the objective is to determine if xis present in the list A2. Algorithm Algorithm 1. Linear Search Algorithm Iterate through nnumbers to …

(4.12)全面解析-SQL事务+隔离级别+阻塞+死锁

30分钟全面解析-SQL事务隔离级别阻塞死锁 转自&#xff1a;https://blog.csdn.net/slowlifes/article/details/52752735 2016年10月07日 23:17:46 阅读数&#xff1a;1097阅读目录 概述&#xff1a;一、事务二、锁三、阻塞四、隔离级别五.死锁以前总是追求新东西&#xff0c;发…

抽屉原理

“抽屉原理”最先是由19世纪的德国数学家迪里赫莱&#xff08;Dirichlet&#xff09;运用于解决数学问题的&#xff0c;所以又称“迪里赫莱原理”&#xff0c;也有称“鸽巢原理”的。这个原理可以简单地叙述为“把10个苹果&#xff0c;任意分放在9个抽屉里&#xff0c;则至少有…

jdbc之连接Oracle的基本步骤

1 // 1.加载驱动程序2 Class.forName("oracle.jdbc.driver.OracleDriver");3 // 2.获取数据库连接4 Connection conn DriverManager.getConnection("jdbc:oracle:thin:localhost:1521:orcl", "j0408", "1");5 // 3.构建SQL语句6 Stri…

[jzoj 5775]【NOIP2008模拟】农夫约的假期 (前缀和+递推)

传送门 Description 在某国有一个叫农夫约的人&#xff0c;他养了很多羊&#xff0c;其中有两头名叫mm和hh&#xff0c;他们的歌声十分好听&#xff0c;被当地人称为“魔音” 农夫约也有自己的假期呀&#xff01;他要去海边度假&#xff0c;然而mm和hh不能离开他。没办法&#…

mac启动mysql,apache,php

在用php编写网站之前&#xff0c;先要启动之前搭建好的环境。 1.启动mysql &#xff08;1&#xff09;在system preference的最底部有mysql控制图标&#xff1a; &#xff08;2&#xff09;点击mysql图标&#xff0c;启动mysql&#xff1a; 2.启动apache&#xff1a; 在term…

如何在命令长度受限的情况下成功get到webshell(函数参数受限突破、mysql的骚操作)...

0x01 问题提出 还记得上篇文章记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门)&#xff0c;我们讲到了一些PHP的一些如何巧妙地绕过数字和字母受限的技巧&#xff0c;今天我要给大家分享的是如何在命令长度受限的情况下成功get到webshell&#xff0c;以及关…

iOS开发造轮子 | 通用占位图

https://www.jianshu.com/p/beca3ac24031 实际运用场景&#xff1a; 没网时的提示view&#xff0c;tableView或collectionView没内容时的展示view&#xff0c;以及其它特殊情况时展示的特定view。如&#xff1a; 常见的几种情况我的目标&#xff1a; 对以上几种情况的展示view做…

java 计算26个字母在一段文本中出现的频率(保留小数点后4位)

public class FrequencyCalculator {public static void main(String[] args){//定义需要计算字母出现频率的文本String text"some off#acebooksea3rl255 yinvestorssoldofftheirstockatthefirs" "tchancetheygotbutceomarkzuckerbergishangingontohissharesfor…

Maven_生产环境下开发对Maven的需求

目前技术开发时存在的问题&#xff1a; 一个项目就是一个工程 如果一个项目非常庞大&#xff0c;就不适合继续使用package来划分模块。最好是每一个模块对应一个工程&#xff0c;利于分工协作。 借助Maven就可以将一个项目拆分多个工程。 项目中需要的jar包必须手动复制粘贴到W…

string赋值-单引号和双引号的区别(php)

在赋予一个string值的时候&#xff0c;可以用单引号或者双引号。 1.单引号和双引号的区别&#xff1a; 单引号&#xff1a;不会翻译变量。 双引号&#xff1a;会翻译变量&#xff0c;会将变量替换为之前赋予变量的值。 例子&#xff1a; &#xff08;1&#xff09;单引号&a…

Web项目开发流程 PC端

Web项目开发流程 PC端 转自 https://www.cnblogs.com/mdengcc/p/6475733.html一直再做前端&#xff0c;突然想到如果有一天领导让自己独立承担一个web 项目的话是否有足够的能力去接这个任务&#xff0c;要学会自己去搭建一些基础的工具信息。所有的这一切在心里都要有个大致的…

Hibernate常用的Java数据类型映射到mysql和Oracle

研究了常用的Java基本数据类型在mysql和oracle数据库的映射类型。这里使用的是包装类型做研究&#xff0c;一般在hibernate声明的时候最好不要用基本类型&#xff0c;因为数据库中的null空数据有可能映射为基本类型的时候会报错&#xff0c;但是映射到包装类型的时候值为null&a…

Spring Cloud 之 Feign 使用HTTP请求远程服务

Feign是从Netflix中分离出来的轻量级项目&#xff0c;能够在类接口上添加注释&#xff0c;成为一个REST API 客户端&#xff0c;Feign默认集成了Ribbon。 Feign中对 Hystrix 有依赖关系。Feign只是一个便利的rest框架&#xff0c;简化调用&#xff0c;最后还是通过ribbon在注册…

UI控件之UISlider

一、创建 UISlider *slider [[UISlider alloc] initWithFrame:CGRectMake(0, 200, self.view.frame.size.width-100, 50)]; 二、设置最大最小值 slider.minimumValue 0; slider.maximumValue 1; 三、改变圆形前面和后面的颜色 slider.minimumTrackTintColor [UIColor orang…

Front End Accessibility Development Guide

Header Carefully write heading(h1,h2,h3) for screen reader. Alternative Image Provide alt text for both essential and functional images. Provide empty alt text (alt””) for decorative images. Don’t repeat the alt text of an image in the adjacent text. De…