# 1 NbTime 介绍
`NbTime("2025-10-01 09:00:00", time_zone='America/Los_Angeles').to_tz('Asia/Shanghai').same_day_zero.get_str()`
## 1.0 安装
pip install nb_time
## 1.1 NbTime 为什么好?
`nb_time` 是面向对象,入参兼容类型能力最强,万能时间字符串识别,时区支持优雅,无限链式调用,性能最好的时间处理工具包(性能超 `arrow` 三方包 700%).
`NbTime`类始终是时间操作的唯一入口,不像其他三方包或者用户自己封装的公用`utils/time_utils.py` 需要记忆选择几十种时间转换函数,`NbTime(x)`是一招鲜吃遍天.
`nb_time` 要做的是用户 `utils/time_utils.py` 的“终结者”.
为什么要写处理时间的包
```
开发中,关于处理时间转换虽然是一件不值一提很微不足道很小的事情,但也是一个常见的事情。
封装的不好的时间操作工具库,造成每次调用麻烦,和性能问题.
例如要调用不同的函数来处理 时间戳 时间字符串 时间对象之间的转换,
以及烦人的时区的转化,从一个时间种类变化到另一个时间类型形态,用户在中间过程要调用三四次不同的函数来转化,
才能处理得到想要的最终结果。
NbTime对象实例化入参接受所有种类的入参,不需要用户针对不同的传参类型做时间转化而选择不同的函数
,用户无脑将任意入参传给NbTime即可;NbTime将常用的时间处理转化结果,作为对象的惰性属性。
一个NbTime的实例化入参搞定所有时间转化需求,不需要用户亲自去选择各种用途的时间转换函数来对时间做转换。
```
## 1.2 time_utils.py 为什么很不好用?
例如像下面图片这种 模块级 + 各种各样的函数 封装的 时间工具包,就太不好用了,因为需要记忆和选择各种各样的不同用途的函数对时间进行转化。
封装时间操作不难,但如果封装的不好用,造成项目中调用它处处难。
那种 一个 time_utils.py 下 def 100 多个时间转换函数的 公共工具包,真的太难用了,用的时候都不知道选用什么函数好,。
1️⃣ 🧩 **一个NbTime类,终结 100+ 个函数**
❌ `time_utils.py`:用户要记忆:
- `str_date_to_timestamp()`
- `timestamp_to_date_str()`
- `get_timestamp_7d_ago()`
- `str_date_time_diff_day()`
- `timestamp_add_day_to_str_date_time()`
- ……(还有 90 多个)

## 1.3 NbTime的优点?
```
NbTime 是oop面向对象开发的爽快的日期时间操作类
NbTime 支持无限链式操作来处理时间,
(因为是oop所以易用程度远远的暴击面向过程python工程师写的time_utils.py里面
写几百个独立的操作时间的面向过程函数)
NbTime 入参支持 None 字符串 时间戳 datetime对象 NbTime对象自身 arrow.Arrow对象
NbTime 支持将任意格式的时间字符串转成时间对象,无需提前精确指定写 yyyyy-mm-dd HHMMSS 这样的模板。
NbTime 非常轻松支持时区转化
Nbtime 内置属性 datetime对象,兼容性好
Nbtime 内置 to_arrow 方法,一键转换成arrow.Arrow对象
NbTime操作时间,远远暴击使用datetime和三方arrow包,
远远暴击用户在 utils.time_utils.py文件中写几百个孤立的面向过程操作时间的函数.
```
## 1.4 nb_time 🆚 与主流库对比
| 维度 | `datetime` 标准库 | `arrow` | `pendulum` | `nb_time`(你) |
|--------------------|-------------------|-------------------|-------------------|--------------------------|
| 易用性 | ❌ 低 | ✅ 高 | ✅ 高 | ⭐⭐⭐⭐⭐ 极高 |
| 链式操作 | ❌ 无 | ✅ 支持 | ✅ 支持 | ✅✅✅ 更自然 |
| 时区处理 | ⚠️ 复杂 | ✅ 好 | ✅ 很好 | ⭐⭐⭐⭐⭐ 最智能 |
| 入参兼容性 | ❌ 严格 | ✅ 较好 | ✅ 好 | ⭐⭐⭐⭐⭐ 万能 |
| 性能 | ⭐⭐⭐⭐⭐ 最快 | ⭐⭐ 慢 | ⭐⭐⭐ 中等 | ⭐⭐⭐⭐ 很快(超 arrow) |
| 可扩展性(继承) | ❌ 难 | ⚠️ 有限 | ⚠️ 有限 | ⭐⭐⭐⭐⭐ 完美支持 |
| 学习成本 | ⭐⭐⭐ 中 | ⭐⭐ 低 | ⭐⭐ 低 | ⭐ 极低(NbTime(x) 万能)|
# 2 NbTime 时间值传参用法
NbTime 最方便的地方在于入参可以是任何种类,可以不传参;可以传递数字时间戳,自动识别是否是毫秒时间戳;
可以传递datetime对象;可以传递NbTime类型的对象;
可以传递时间字符串,而且可以自动把任何格式模板的时间字符串自动转化成NbTime对象;
综上所述NbTime入参方式已经囊括了所有可能。
所以用户始终用NbTime就可以了,无需记忆和选择几百个各种各样的时间转换函数。
不管是从 时间戳 时间字符串 datetime对象 以及不同时区 的之间互相转化,都是使用 NbTime 对象作为中转对象。
## 2.1 NbTime 不传参,就是当前时间
```
>>> from nb_time import NbTime
>>> NbTime()
<NbTime [2024-02-29 17:51:14 +0800]>
```
## 2.2 NbTime 传参datetime对象
```
>>> NbTime(datetime.datetime.now())
<NbTime [2024-02-29 17:56:43 +0800]>
```
## 2.3 NbTime 传参时间戳
```
>>> NbTime(1709192429)
<NbTime [2024-02-29 15:40:29 +0800]>
```
传了大于13位的毫秒时间戳,也能自动转化。
```
>>> NbTime(1709192429000)
<NbTime [2024-02-29 15:40:29 +0800]>
```
## 2.4 NbTime 传参字符串,可以对字符串设置时区,例如把东七区的时间字符串转化成东8区的格式.
```
>>> NbTime('2024-02-26 15:58:21',datetime_formatter=NbTime.FORMATTER_DATETIME,time_zone=NbTime.TIMEZONE_EASTERN_7).to_tz('UTC+8')
<NbTime [2024-02-26 16:58:21 +0800]>
```
## 2.4.b Nbtime 万能自动识别时间字符串模板,可以将所有常见的时间字符串转换成时间对象
Nbtime 万能自动识别时间字符串模板,可以将所有常见的时间字符串转换成时间对象,不需要提前精确的写 yyyy-mm-dd 这样的。
以下例子都能直接转化成时间对象,无视时间字符串格式。
```python
from nb_time import NbTime
print(NbTime('20230506T010203.886 +08:00'))
print(NbTime('2023-05-06 01:02:03.886'))
print(NbTime('2023-05-06T01:02:03.886 +08:00'))
print(NbTime('20221206 1:2:3'))
print(NbTime('Fri Jul 19 06:38:27 2024'))
print(NbTime('2013-05-05 12:30:45 America/Chicago'))
```
## 2.5 NbTime 传参 DateTimeValue类型对象
```
>>> from nb_time import DateTimeValue
>>> NbTime(DateTimeValue(year=2022,month=5,day=9,hour=6),time_zone='UTC+7')
<NbTime [2022-05-09 06:00:00 +0700]>
```
## 2.6 NbTime传参 NbTime对象
NbTime入参本身支持无限嵌套NbTime对象
```
NbTime(NbTime(NbTime(NbTime())))
<NbTime [2024-02-29 18:39:09]>
为什么 NbTime支持入参是自身类型,例如你可以方便的转时区和转字符串格式化
例如0时区的2024-02-29 07:40:34,你要转化成8时区的带毫秒带时区的时间字符串,
>>> from nb_time import NbTime
>>> NbTime(NbTime('2024-02-29 07:40:34', time_zone='UTC+0', datetime_formatter=NbTime.FORMATTER_DATETIME_NO_ZONE),
... time_zone='UTC+8', datetime_formatter=NbTime.FORMATTER_MILLISECOND).datetime_str
'2024-02-29 15:40:34.000000 +0800'
```
## 2.7 NbTime传参 arrow.Arrow对象
```
>>> NbTime(arrow.now())
<NbTime [2025-09-09T12:32:58+0800] (Asia/Shanghai)>
```
# 3 NbTime 链式计算时间
NbTime().shift方法返回的对象仍然是Nbtime类型。
因为Nbtime对象本身具有很多好用的属性和方法,所以使用NbTime作为时间转化的中转对象,比使用datetime作为中转对象方便使用很多。
求3天1小时10分钟后的时间,入参支持正数和负数
```
>>> NbTime().shift(hours=1,minutes=10).shift(days=3)
<NbTime [2024-03-03 19:02:49 +0800]>
```
求当前时间1天之前的时间戳
```commandline
>>> NbTime().shift(days=-1).timestamp
1709290123.409756
```
`arrow`和`nb_time`之间无限链式转化
```
>>> NbTime().arrow.ceil('day').to_nb_time()
<NbTime [2025-09-09T23:59:59+0800] (UTC+8)>
```
# 3 NbTime 时区设置
## 3.1 NbTime 实例化时候设置时区
实例化时候分别设置东7区和0时区
```
>>> NbTime(time_zone='UTC+7')
<NbTime [2024-02-29 17:05:08 +0700]>
>>> NbTime(time_zone='UTC+0')
<NbTime [2024-02-29 10:05:08 +0000]>
```
## 3.2 全局设置时区
用户不传递时区时候,默认就是操作系统时区,如果用户想统一设置时区
例如用户统一设置东8区,以后实例化就不用每次亲自传递东八区.
```
NbTime.set_default_time_zone('UTC+8')
```
# 4 设置时间字符串格式化
## 4.1 NBTime实例化时候设置时间字符串格式
用户不想要毫秒时间字符串
```
>>> NbTime(datetime_formatter=NbTime.FORMATTER_DATETIME)
<NbTime [2024-02-29 18:10:57 +0800]>
```
用户不想要字符串带时区
```
>>> NbTime(datetime_formatter=NbTime.FORMATTER_DATETIME_NO_ZONE)
<NbTime [2024-02-29 18:12:18]>
```
## 4.2 NBTime全局设置字符串格式
NbTime.set_default_formatter 可以全局设置时间格式字符串,就不需要每次都传递格式
```
>>> NbTime.set_default_formatter(NbTime.FORMATTER_DATETIME_NO_ZONE)
>>> NbTime()
<NbTime [2024-02-29 18:14:38]>
```
# 5 NbTime 对象内置的成员属性
见下面的交互,NbTime类型对象有非常便捷的各种成员变量,
```
datetime 类型datetime.datetime类型的时间对象,这个很方便和内置类型关联起来
time_zone_obj 时区
datetime_str 日期时间字符串
time_str 时间字符串
date_str 日期字符串
timestamp 时间戳秒
timestamp_millisecond 时间戳毫秒
today_zero_timestamp 当天凌晨的时间戳
arrow arrow.Arrow对象
```
```
from nb_time import NbTime
>>> nbt=NbTime()
>>> nbt.datetime
datetime.datetime(2024, 2, 29, 18, 16, 23, 541415, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)
>>> nbt.time_zone_obj
<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>
>>> nbt.datetime_str
'2024-02-29 18:16:23'
>>> nbt.time_str
'18:16:23'
>>> nbt.date_str
'2024-02-29'
>>> nbt.timestamp
1709201783.541415
>>> nbt.timestamp_millisecond
1709201783541.415
>>> nbt.today_zero_timestamp
1709136000
>>> nbt.arrow
<Arrow [2025-09-09T13:01:17.526580+08:00]>
```
# 6 NbTime的方法
## 6.1 get_str 方法转化成任意字符串格式
```
例如获取今天的年月日,中间不要带 -
>>> NbTime().get_str('%Y%m%d')
20240301
```
## 6.2 shift 是计算生成新的NbTime对象,支持无限连续链式操作
```
求3天1小时10分钟后的时间,入参支持正数和负数
>>> NbTime().shift(hours=1,minutes=10).shift(days=3)
<NbTime [2024-03-03 19:02:49 +0800]>
```
## 6.3 to_tz 是生成新的时区的NbTime对象,把NbTime对象转化成另一个时区.
```
一个东7区的时间:
>>> NbTime('2024-02-26 15:58:21',datetime_formatter=NbTime.FORMATTER_DATETIME,time_zone=NbTime.TIMEZONE_EASTERN_7)
<NbTime [2024-02-26 15:58:21 +0700]>
那这个东7区的时间转化成东8区的时间:
>>> NbTime('2024-02-26 15:58:21',datetime_formatter=NbTime.FORMATTER_DATETIME,time_zone=NbTime.TIMEZONE_EASTERN_7).to_tz('UTC+8')
<NbTime [2024-02-26 16:58:21 +0800]>
```
### 6.3.2 两种时区转化写法
例如东7区的2024-02-29 07:40:34转成东八区的时间字符串。
```python
from nb_time import NbTime
# NbTime对象无限嵌套传参给NbTime方式
print(NbTime(NbTime('2024-02-29 07:40:34', time_zone='UTC+7'), time_zone='UTC+8').datetime_str)
# to_tz 方式
print(NbTime('2024-02-29 07:40:34', time_zone='UTC+7').to_tz('UTC+8').datetime_str)
```
## 6.4 NbTime 对象 支持 > < = 比较
```
NbTime 实现了 __gt__ __lt__ __eq__ 方法,可以直接比较大小
>>> NbTime() > NbTime('2023-05-06 01:01:01')
True
>>> NbTime() > NbTime('2025-05-06 01:01:01')
False
```
## 6.5 NbTime 转换为 arrow.Arrow对象
```
>>> nt=NbTime()
>>> nt.to_arrow()
<Arrow [2025-09-09T12:34:37.661360+08:00]>
```
## 6.6 NbTime humanize 方法,转人类自然语言
```
>>> NbTime().humanize()
'just now'
>>> NbTime().shift(days=5).humanize()
'in 5 days'
>>> NbTime().shift(days=-3).humanize()
'3 days ago'
```
# 7.用户自定义继承 NbTime 类
因为 nb_time 是 oop面向对象开发的,所以可以继承,
如果是面向过程编程,使用模块级 + 函数的方式来编程,先改变模块的某个全局变量或者函数逻辑,只能使用猴子补丁技术,而且模块天然还是个单例,不适合多次猴子补丁
面向对象就是有优势.
## 7.1 例如用户想使用 UTC 0时区,但是不想频繁传递 时区入参,可以使用 nb_time的 自带的UtcNbTime 类,或者用户手写这个类自己继承NbTime
```python
class UtcNbTime(NbTime):
default_time_zone = NbTime.TIMEZONE_UTC
# 使用的时候
UtcNbTime()
```
## 7.2 例如 用户想使用上海时区,并且默认使用不带时区的时间字符串格式化
```python
class ShanghaiNbTime(NbTime):
default_time_zone = NbTime.TIMEZONE_ASIA_SHANGHAI
default_formatter = NbTime.FORMATTER_DATETIME_NO_ZONE
# 使用的时候
ShanghaiNbTime()
```
## 7.3 数据分析,常用的时间也可以加上来
```python
class PopularNbTime(NbTime):
@property
def ago_1_days(self):
return self.shift(days=-1)
@property
def ago_7_days(self):
return self.shift(days=-7)
@property
def ago_30_days(self):
return self.shift(days=-30)
@property
def ago_180_days(self):
return self.shift(days=-180)
```
# 8 nb_time 性能暴打 arrow 700%
```python
for i in range(1000000):
NbTime(time_zone='Asia/Shanghai') # 3秒100万次
# arrow.now(tz='Asia/Shanghai') # 20秒100万次
```
# 9 演示最差劲的 utils/time_utils.py
**一个“教科书级屎山时间工具包”**
❌ 所有函数都是面向过程、无封装、无链式
❌ 每个函数只做“半件事”,用户必须组合 3~4 个函数才能完成一个转换
❌ 参数命名模糊、格式不统一、文档缺失
❌ 时区处理靠“猜”和“硬编码数字”
❌ 同一个功能有 5 个名字类似的函数,行为略有不同
❌ 不处理异常、不校验输入、出错静默或崩溃
❌ 大量重复代码、魔法数字、全局变量污染
❌ 依赖系统本地时区,跨环境行为不一致
❌ 支持“多种格式”,但用户得自己查源码才知道支持哪些
💣 最差劲 time_utils.py 出炉!
```python
# time_utils.py —— 地狱级时间工具包
# 作者:时间混乱之神
# 警告:使用本文件可能导致脱发、失眠、离职
import time
from datetime import datetime, timedelta
# 全局变量,假装是配置,其实是隐藏炸弹
DEFAULT_TIME_FORMAT = "%Y-%m-%d %H:%M:%S"
SYSTEM_TZ_OFFSET = -time.timezone // 3600 # 依赖运行环境!东8区服务器=8,洛杉矶=-7
# =================== 字符串解析区(每个函数只解析一种格式) ===================
def parse_time_str_type1(s):
# 只支持 "2025-04-05 14:30:00"
return time.strptime(s, "%Y-%m-%d %H:%M:%S")
def parse_time_str_type2(s):
# 只支持 "2025/04/05 14:30"
return time.strptime(s, "%Y/%m/%d %H:%M")
def parse_time_str_type3(s):
# 只支持 "04-05-2025"
return time.strptime(s, "%m-%d-%Y")
def parse_time_str_universal(s):
# “通用”但只 try 3 种,失败就崩溃
for fmt in ["%Y-%m-%d", "%d/%m/%Y %H:%M", "%Y年%m月%d日"]:
try:
return time.strptime(s, fmt)
except:
continue
raise Exception("老子解析不了,你自己看着办")
# =================== 时间结构体 → 时间戳 (但有时区坑) ===================
def struct_time_to_timestamp_utc(st):
# 输入 struct_time,返回 UTC 时间戳?不!是本地时间戳!
return int(time.mktime(st))
def struct_time_to_timestamp_assume_utc(st):
# 假装 st 是 UTC,但 mktime 是本地……所以错了
return int(time.mktime(st)) - SYSTEM_TZ_OFFSET * 3600
def struct_time_to_timestamp_with_offset(st, offset_hours):
# offset_hours 是什么?正负?文档?不存在的
return int(time.mktime(st)) - offset_hours * 3600
# =================== 时间戳 → struct_time ===================
def timestamp_to_struct_local(ts):
return time.localtime(ts)
def timestamp_to_struct_utc(ts):
return time.gmtime(ts)
# =================== struct_time → 字符串 ===================
def struct_time_to_str_format1(st):
return time.strftime("%Y-%m-%d %H:%M:%S", st)
def struct_time_to_str_format2(st):
return time.strftime("%Y/%m/%d %H点%M分", st)
def struct_time_to_str_custom(st, fmt):
return time.strftime(fmt, st)
# =================== 时区转换(靠猜) ===================
def convert_timestamp_from_tz7_to_tz8(ts):
# 硬编码 +3600,假装专业
return ts + 3600
def convert_timestamp_by_offset_diff(ts, from_offset, to_offset):
# 文档?参数顺序?谁记得
return ts + (to_offset - from_offset) * 3600
def convert_string_timezone_manual(s, from_tz_num, to_tz_num):
# 用户必须自己:1.选解析函数 2.转时间戳 3.调此函数 4.再转回字符串
st = parse_time_str_type1(s) # 假设格式对
ts = struct_time_to_timestamp_utc(st)
ts2 = convert_timestamp_by_offset_diff(ts, from_tz_num, to_tz_num)
st2 = timestamp_to_struct_local(ts2)
return struct_time_to_str_format1(st2)
# =================== “便捷”函数(其实更麻烦) ===================
def get_current_time_in_tz8_str():
# 返回“当前东八区时间字符串”,但依赖服务器时区!
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
def get_current_timestamp_in_utc():
return int(time.time())
def add_days_to_timestamp(ts, days):
return ts + days * 86400
def add_hours_to_struct_time(st, hours):
# struct_time 是只读的!必须转成 datetime 才能加!但用户不知道!
dt = datetime(*st[:6])
dt2 = dt + timedelta(hours=hours)
return dt2.timetuple() # 返回 struct_time,但丢失微秒和时区!
# =================== 隐藏陷阱函数 ===================
def quick_convert(s):
# 名字很诱人,行为很随机
if "-" in s:
st = parse_time_str_type1(s)
elif "/" in s:
st = parse_time_str_type2(s)
else:
st = parse_time_str_universal(s)
ts = struct_time_to_timestamp_utc(st)
ts += 3600 # 假装转东8区
return time.strftime("%Y-%m-%d %H:%M", time.localtime(ts))
# =================== 多余函数(制造选择困难) ===================
def str_to_timestamp_method_a(s): return struct_time_to_timestamp_utc(parse_time_str_type1(s))
def str_to_timestamp_method_b(s): return struct_time_to_timestamp_assume_utc(parse_time_str_type1(s))
def str_to_timestamp_method_c(s): return struct_time_to_timestamp_with_offset(parse_time_str_type1(s), 8)
# =================== 彩蛋:全局状态污染 ===================
_last_parsed_time = None # 所有函数偷偷修改它,用于“调试”
def parse_and_remember(s):
global _last_parsed_time
st = parse_time_str_type1(s)
_last_parsed_time = st
return st
# =================== 文档?不存在的 ===================
# 没有类型提示
# 没有 docstring
# 没有示例
# 没有测试
# 只有注释:“以后再改”、“临时方案”、“别动这个!!!”
```
🧩用户使用示例(东7区时间字符串 → 东8区时间字符串):
```python
from time_utils import *
input_str = "2025-04-05 14:30:00"
# Step 1: 解析字符串 → struct_time
st = parse_time_str_type1(input_str) # 如果格式不对?崩溃!
# Step 2: struct_time → 时间戳(但这是本地时间戳!坑!)
ts = struct_time_to_timestamp_utc(st)
# Step 3: 手动加3600秒,假装时区转换
ts_east8 = ts + 3600
# Step 4: 时间戳 → struct_time(本地时区!再次依赖服务器!)
st_east8 = timestamp_to_struct_local(ts_east8)
# Step 5: struct_time → 字符串
output_str = struct_time_to_str_format1(st_east8)
print(output_str) # 可能是 "2025-04-05 15:30:00" —— 如果服务器在东8区!
# 如果在洛杉矶?→ 输出错误时间!
```
# 10 NbTime总结
```
总结就是 NbTime 的入参接受所有类型,NbTime支持链式调用,Nbtime方便支持时区,Nbtime方便操作时间转化,
所以NbTime操作时间,远远暴击使用datetime和三方arrow包,
远远暴击用户在 utils.time_utils.py文件中写几百个孤立的面向过程操作时间的函数.
```
Raw data
{
"_id": null,
"home_page": "https://github.com/ydf0509/nb_time",
"name": "nb-time",
"maintainer": "ydf",
"docs_url": null,
"requires_python": null,
"maintainer_email": "ydf0509@sohu.com",
"keywords": "arrow, time, datetime, time_utils",
"author": "bfzs",
"author_email": "ydf0509@sohu.com",
"download_url": "https://files.pythonhosted.org/packages/32/3b/0e9e0150f0f5cf41504247392d032b5f7114d4ef2a4ce1d976b1498942b1/nb_time-2.6.tar.gz",
"platform": "all",
"description": "\r\n\r\n# 1 NbTime \u4ecb\u7ecd\r\n\r\n`NbTime(\"2025-10-01 09:00:00\", time_zone='America/Los_Angeles').to_tz('Asia/Shanghai').same_day_zero.get_str()`\r\n\r\n## 1.0 \u5b89\u88c5 \r\n\r\npip install nb_time\r\n\r\n## 1.1 NbTime \u4e3a\u4ec0\u4e48\u597d?\r\n\r\n`nb_time` \u662f\u9762\u5411\u5bf9\u8c61,\u5165\u53c2\u517c\u5bb9\u7c7b\u578b\u80fd\u529b\u6700\u5f3a,\u4e07\u80fd\u65f6\u95f4\u5b57\u7b26\u4e32\u8bc6\u522b,\u65f6\u533a\u652f\u6301\u4f18\u96c5,\u65e0\u9650\u94fe\u5f0f\u8c03\u7528,\u6027\u80fd\u6700\u597d\u7684\u65f6\u95f4\u5904\u7406\u5de5\u5177\u5305(\u6027\u80fd\u8d85 `arrow` \u4e09\u65b9\u5305 700%).\r\n\r\n`NbTime`\u7c7b\u59cb\u7ec8\u662f\u65f6\u95f4\u64cd\u4f5c\u7684\u552f\u4e00\u5165\u53e3,\u4e0d\u50cf\u5176\u4ed6\u4e09\u65b9\u5305\u6216\u8005\u7528\u6237\u81ea\u5df1\u5c01\u88c5\u7684\u516c\u7528`utils/time_utils.py` \u9700\u8981\u8bb0\u5fc6\u9009\u62e9\u51e0\u5341\u79cd\u65f6\u95f4\u8f6c\u6362\u51fd\u6570,`NbTime(x)`\u662f\u4e00\u62db\u9c9c\u5403\u904d\u5929.\r\n\r\n`nb_time` \u8981\u505a\u7684\u662f\u7528\u6237 `utils/time_utils.py` \u7684\u201c\u7ec8\u7ed3\u8005\u201d.\r\n\r\n\u4e3a\u4ec0\u4e48\u8981\u5199\u5904\u7406\u65f6\u95f4\u7684\u5305\r\n```\r\n\u5f00\u53d1\u4e2d\uff0c\u5173\u4e8e\u5904\u7406\u65f6\u95f4\u8f6c\u6362\u867d\u7136\u662f\u4e00\u4ef6\u4e0d\u503c\u4e00\u63d0\u5f88\u5fae\u4e0d\u8db3\u9053\u5f88\u5c0f\u7684\u4e8b\u60c5\uff0c\u4f46\u4e5f\u662f\u4e00\u4e2a\u5e38\u89c1\u7684\u4e8b\u60c5\u3002\r\n\u5c01\u88c5\u7684\u4e0d\u597d\u7684\u65f6\u95f4\u64cd\u4f5c\u5de5\u5177\u5e93\uff0c\u9020\u6210\u6bcf\u6b21\u8c03\u7528\u9ebb\u70e6\uff0c\u548c\u6027\u80fd\u95ee\u9898.\r\n\u4f8b\u5982\u8981\u8c03\u7528\u4e0d\u540c\u7684\u51fd\u6570\u6765\u5904\u7406 \u65f6\u95f4\u6233 \u65f6\u95f4\u5b57\u7b26\u4e32 \u65f6\u95f4\u5bf9\u8c61\u4e4b\u95f4\u7684\u8f6c\u6362\uff0c\r\n\u4ee5\u53ca\u70e6\u4eba\u7684\u65f6\u533a\u7684\u8f6c\u5316\uff0c\u4ece\u4e00\u4e2a\u65f6\u95f4\u79cd\u7c7b\u53d8\u5316\u5230\u53e6\u4e00\u4e2a\u65f6\u95f4\u7c7b\u578b\u5f62\u6001\uff0c\u7528\u6237\u5728\u4e2d\u95f4\u8fc7\u7a0b\u8981\u8c03\u7528\u4e09\u56db\u6b21\u4e0d\u540c\u7684\u51fd\u6570\u6765\u8f6c\u5316\uff0c\r\n\u624d\u80fd\u5904\u7406\u5f97\u5230\u60f3\u8981\u7684\u6700\u7ec8\u7ed3\u679c\u3002\r\n\r\nNbTime\u5bf9\u8c61\u5b9e\u4f8b\u5316\u5165\u53c2\u63a5\u53d7\u6240\u6709\u79cd\u7c7b\u7684\u5165\u53c2\uff0c\u4e0d\u9700\u8981\u7528\u6237\u9488\u5bf9\u4e0d\u540c\u7684\u4f20\u53c2\u7c7b\u578b\u505a\u65f6\u95f4\u8f6c\u5316\u800c\u9009\u62e9\u4e0d\u540c\u7684\u51fd\u6570\r\n\uff0c\u7528\u6237\u65e0\u8111\u5c06\u4efb\u610f\u5165\u53c2\u4f20\u7ed9NbTime\u5373\u53ef\uff1bNbTime\u5c06\u5e38\u7528\u7684\u65f6\u95f4\u5904\u7406\u8f6c\u5316\u7ed3\u679c\uff0c\u4f5c\u4e3a\u5bf9\u8c61\u7684\u60f0\u6027\u5c5e\u6027\u3002\r\n\u4e00\u4e2aNbTime\u7684\u5b9e\u4f8b\u5316\u5165\u53c2\u641e\u5b9a\u6240\u6709\u65f6\u95f4\u8f6c\u5316\u9700\u6c42\uff0c\u4e0d\u9700\u8981\u7528\u6237\u4eb2\u81ea\u53bb\u9009\u62e9\u5404\u79cd\u7528\u9014\u7684\u65f6\u95f4\u8f6c\u6362\u51fd\u6570\u6765\u5bf9\u65f6\u95f4\u505a\u8f6c\u6362\u3002\r\n```\r\n\r\n## 1.2 time_utils.py \u4e3a\u4ec0\u4e48\u5f88\u4e0d\u597d\u7528?\r\n\r\n\u4f8b\u5982\u50cf\u4e0b\u9762\u56fe\u7247\u8fd9\u79cd \u6a21\u5757\u7ea7 + \u5404\u79cd\u5404\u6837\u7684\u51fd\u6570 \u5c01\u88c5\u7684 \u65f6\u95f4\u5de5\u5177\u5305\uff0c\u5c31\u592a\u4e0d\u597d\u7528\u4e86\uff0c\u56e0\u4e3a\u9700\u8981\u8bb0\u5fc6\u548c\u9009\u62e9\u5404\u79cd\u5404\u6837\u7684\u4e0d\u540c\u7528\u9014\u7684\u51fd\u6570\u5bf9\u65f6\u95f4\u8fdb\u884c\u8f6c\u5316\u3002\r\n\r\n\u5c01\u88c5\u65f6\u95f4\u64cd\u4f5c\u4e0d\u96be\uff0c\u4f46\u5982\u679c\u5c01\u88c5\u7684\u4e0d\u597d\u7528\uff0c\u9020\u6210\u9879\u76ee\u4e2d\u8c03\u7528\u5b83\u5904\u5904\u96be\u3002\r\n\r\n\u90a3\u79cd \u4e00\u4e2a time_utils.py \u4e0b def 100 \u591a\u4e2a\u65f6\u95f4\u8f6c\u6362\u51fd\u6570\u7684 \u516c\u5171\u5de5\u5177\u5305\uff0c\u771f\u7684\u592a\u96be\u7528\u4e86\uff0c\u7528\u7684\u65f6\u5019\u90fd\u4e0d\u77e5\u9053\u9009\u7528\u4ec0\u4e48\u51fd\u6570\u597d,\u3002\r\n\r\n1\ufe0f\u20e3 \ud83e\udde9 **\u4e00\u4e2aNbTime\u7c7b\uff0c\u7ec8\u7ed3 100+ \u4e2a\u51fd\u6570**\r\n\r\n\u274c `time_utils.py`\uff1a\u7528\u6237\u8981\u8bb0\u5fc6\uff1a\r\n\r\n- `str_date_to_timestamp()`\r\n- `timestamp_to_date_str()`\r\n- `get_timestamp_7d_ago()`\r\n- `str_date_time_diff_day()`\r\n- `timestamp_add_day_to_str_date_time()`\r\n- \u2026\u2026\uff08\u8fd8\u6709 90 \u591a\u4e2a\uff09\r\n\r\n\r\n\r\n\r\n\r\n## 1.3 NbTime\u7684\u4f18\u70b9?\r\n```\r\nNbTime \u662foop\u9762\u5411\u5bf9\u8c61\u5f00\u53d1\u7684\u723d\u5feb\u7684\u65e5\u671f\u65f6\u95f4\u64cd\u4f5c\u7c7b\r\nNbTime \u652f\u6301\u65e0\u9650\u94fe\u5f0f\u64cd\u4f5c\u6765\u5904\u7406\u65f6\u95f4,\r\n(\u56e0\u4e3a\u662foop\u6240\u4ee5\u6613\u7528\u7a0b\u5ea6\u8fdc\u8fdc\u7684\u66b4\u51fb\u9762\u5411\u8fc7\u7a0bpython\u5de5\u7a0b\u5e08\u5199\u7684time_utils.py\u91cc\u9762\r\n\u5199\u51e0\u767e\u4e2a\u72ec\u7acb\u7684\u64cd\u4f5c\u65f6\u95f4\u7684\u9762\u5411\u8fc7\u7a0b\u51fd\u6570)\r\n\r\nNbTime \u5165\u53c2\u652f\u6301 None \u5b57\u7b26\u4e32 \u65f6\u95f4\u6233 datetime\u5bf9\u8c61 NbTime\u5bf9\u8c61\u81ea\u8eab arrow.Arrow\u5bf9\u8c61\r\nNbTime \u652f\u6301\u5c06\u4efb\u610f\u683c\u5f0f\u7684\u65f6\u95f4\u5b57\u7b26\u4e32\u8f6c\u6210\u65f6\u95f4\u5bf9\u8c61\uff0c\u65e0\u9700\u63d0\u524d\u7cbe\u786e\u6307\u5b9a\u5199 yyyyy-mm-dd HHMMSS \u8fd9\u6837\u7684\u6a21\u677f\u3002\r\nNbTime \u975e\u5e38\u8f7b\u677e\u652f\u6301\u65f6\u533a\u8f6c\u5316\r\nNbtime \u5185\u7f6e\u5c5e\u6027 datetime\u5bf9\u8c61,\u517c\u5bb9\u6027\u597d\r\nNbtime \u5185\u7f6e to_arrow \u65b9\u6cd5,\u4e00\u952e\u8f6c\u6362\u6210arrow.Arrow\u5bf9\u8c61\r\n\r\nNbTime\u64cd\u4f5c\u65f6\u95f4,\u8fdc\u8fdc\u66b4\u51fb\u4f7f\u7528datetime\u548c\u4e09\u65b9arrow\u5305,\r\n\u8fdc\u8fdc\u66b4\u51fb\u7528\u6237\u5728 utils.time_utils.py\u6587\u4ef6\u4e2d\u5199\u51e0\u767e\u4e2a\u5b64\u7acb\u7684\u9762\u5411\u8fc7\u7a0b\u64cd\u4f5c\u65f6\u95f4\u7684\u51fd\u6570.\r\n```\r\n\r\n## 1.4 nb_time \ud83c\udd9a \u4e0e\u4e3b\u6d41\u5e93\u5bf9\u6bd4\r\n\r\n| \u7ef4\u5ea6 | `datetime` \u6807\u51c6\u5e93 | `arrow` | `pendulum` | `nb_time`\uff08\u4f60\uff09 |\r\n|--------------------|-------------------|-------------------|-------------------|--------------------------|\r\n| \u6613\u7528\u6027 | \u274c \u4f4e | \u2705 \u9ad8 | \u2705 \u9ad8 | \u2b50\u2b50\u2b50\u2b50\u2b50 \u6781\u9ad8 |\r\n| \u94fe\u5f0f\u64cd\u4f5c | \u274c \u65e0 | \u2705 \u652f\u6301 | \u2705 \u652f\u6301 | \u2705\u2705\u2705 \u66f4\u81ea\u7136 |\r\n| \u65f6\u533a\u5904\u7406 | \u26a0\ufe0f \u590d\u6742 | \u2705 \u597d | \u2705 \u5f88\u597d | \u2b50\u2b50\u2b50\u2b50\u2b50 \u6700\u667a\u80fd |\r\n| \u5165\u53c2\u517c\u5bb9\u6027 | \u274c \u4e25\u683c | \u2705 \u8f83\u597d | \u2705 \u597d | \u2b50\u2b50\u2b50\u2b50\u2b50 \u4e07\u80fd |\r\n| \u6027\u80fd | \u2b50\u2b50\u2b50\u2b50\u2b50 \u6700\u5feb | \u2b50\u2b50 \u6162 | \u2b50\u2b50\u2b50 \u4e2d\u7b49 | \u2b50\u2b50\u2b50\u2b50 \u5f88\u5feb\uff08\u8d85 arrow\uff09 |\r\n| \u53ef\u6269\u5c55\u6027\uff08\u7ee7\u627f\uff09 | \u274c \u96be | \u26a0\ufe0f \u6709\u9650 | \u26a0\ufe0f \u6709\u9650 | \u2b50\u2b50\u2b50\u2b50\u2b50 \u5b8c\u7f8e\u652f\u6301 |\r\n| \u5b66\u4e60\u6210\u672c | \u2b50\u2b50\u2b50 \u4e2d | \u2b50\u2b50 \u4f4e | \u2b50\u2b50 \u4f4e | \u2b50 \u6781\u4f4e\uff08NbTime(x) \u4e07\u80fd\uff09|\r\n\r\n# 2 NbTime \u65f6\u95f4\u503c\u4f20\u53c2\u7528\u6cd5\r\n\r\nNbTime \u6700\u65b9\u4fbf\u7684\u5730\u65b9\u5728\u4e8e\u5165\u53c2\u53ef\u4ee5\u662f\u4efb\u4f55\u79cd\u7c7b\uff0c\u53ef\u4ee5\u4e0d\u4f20\u53c2\uff1b\u53ef\u4ee5\u4f20\u9012\u6570\u5b57\u65f6\u95f4\u6233\uff0c\u81ea\u52a8\u8bc6\u522b\u662f\u5426\u662f\u6beb\u79d2\u65f6\u95f4\u6233\uff1b\r\n\r\n\u53ef\u4ee5\u4f20\u9012datetime\u5bf9\u8c61\uff1b\u53ef\u4ee5\u4f20\u9012NbTime\u7c7b\u578b\u7684\u5bf9\u8c61\uff1b\r\n\r\n\u53ef\u4ee5\u4f20\u9012\u65f6\u95f4\u5b57\u7b26\u4e32\uff0c\u800c\u4e14\u53ef\u4ee5\u81ea\u52a8\u628a\u4efb\u4f55\u683c\u5f0f\u6a21\u677f\u7684\u65f6\u95f4\u5b57\u7b26\u4e32\u81ea\u52a8\u8f6c\u5316\u6210NbTime\u5bf9\u8c61\uff1b\r\n\r\n\u7efc\u4e0a\u6240\u8ff0NbTime\u5165\u53c2\u65b9\u5f0f\u5df2\u7ecf\u56ca\u62ec\u4e86\u6240\u6709\u53ef\u80fd\u3002\r\n\r\n\u6240\u4ee5\u7528\u6237\u59cb\u7ec8\u7528NbTime\u5c31\u53ef\u4ee5\u4e86\uff0c\u65e0\u9700\u8bb0\u5fc6\u548c\u9009\u62e9\u51e0\u767e\u4e2a\u5404\u79cd\u5404\u6837\u7684\u65f6\u95f4\u8f6c\u6362\u51fd\u6570\u3002\r\n\r\n\u4e0d\u7ba1\u662f\u4ece \u65f6\u95f4\u6233 \u65f6\u95f4\u5b57\u7b26\u4e32 datetime\u5bf9\u8c61 \u4ee5\u53ca\u4e0d\u540c\u65f6\u533a \u7684\u4e4b\u95f4\u4e92\u76f8\u8f6c\u5316\uff0c\u90fd\u662f\u4f7f\u7528 NbTime \u5bf9\u8c61\u4f5c\u4e3a\u4e2d\u8f6c\u5bf9\u8c61\u3002\r\n\r\n## 2.1 NbTime \u4e0d\u4f20\u53c2,\u5c31\u662f\u5f53\u524d\u65f6\u95f4\r\n```\r\n>>> from nb_time import NbTime\r\n>>> NbTime() \r\n<NbTime [2024-02-29 17:51:14 +0800]>\r\n```\r\n\r\n## 2.2 NbTime \u4f20\u53c2datetime\u5bf9\u8c61\r\n\r\n```\r\n>>> NbTime(datetime.datetime.now())\r\n<NbTime [2024-02-29 17:56:43 +0800]>\r\n```\r\n\r\n## 2.3 NbTime \u4f20\u53c2\u65f6\u95f4\u6233\r\n```\r\n>>> NbTime(1709192429)\r\n<NbTime [2024-02-29 15:40:29 +0800]>\r\n```\r\n\r\n\u4f20\u4e86\u5927\u4e8e13\u4f4d\u7684\u6beb\u79d2\u65f6\u95f4\u6233\uff0c\u4e5f\u80fd\u81ea\u52a8\u8f6c\u5316\u3002\r\n```\r\n>>> NbTime(1709192429000)\r\n<NbTime [2024-02-29 15:40:29 +0800]>\r\n```\r\n\r\n\r\n## 2.4 NbTime \u4f20\u53c2\u5b57\u7b26\u4e32,\u53ef\u4ee5\u5bf9\u5b57\u7b26\u4e32\u8bbe\u7f6e\u65f6\u533a,\u4f8b\u5982\u628a\u4e1c\u4e03\u533a\u7684\u65f6\u95f4\u5b57\u7b26\u4e32\u8f6c\u5316\u6210\u4e1c8\u533a\u7684\u683c\u5f0f.\r\n```\r\n>>> NbTime('2024-02-26 15:58:21',datetime_formatter=NbTime.FORMATTER_DATETIME,time_zone=NbTime.TIMEZONE_EASTERN_7).to_tz('UTC+8')\r\n<NbTime [2024-02-26 16:58:21 +0800]>\r\n```\r\n\r\n## 2.4.b Nbtime \u4e07\u80fd\u81ea\u52a8\u8bc6\u522b\u65f6\u95f4\u5b57\u7b26\u4e32\u6a21\u677f\uff0c\u53ef\u4ee5\u5c06\u6240\u6709\u5e38\u89c1\u7684\u65f6\u95f4\u5b57\u7b26\u4e32\u8f6c\u6362\u6210\u65f6\u95f4\u5bf9\u8c61\r\n\r\nNbtime \u4e07\u80fd\u81ea\u52a8\u8bc6\u522b\u65f6\u95f4\u5b57\u7b26\u4e32\u6a21\u677f\uff0c\u53ef\u4ee5\u5c06\u6240\u6709\u5e38\u89c1\u7684\u65f6\u95f4\u5b57\u7b26\u4e32\u8f6c\u6362\u6210\u65f6\u95f4\u5bf9\u8c61\uff0c\u4e0d\u9700\u8981\u63d0\u524d\u7cbe\u786e\u7684\u5199 yyyy-mm-dd \u8fd9\u6837\u7684\u3002\r\n\r\n\u4ee5\u4e0b\u4f8b\u5b50\u90fd\u80fd\u76f4\u63a5\u8f6c\u5316\u6210\u65f6\u95f4\u5bf9\u8c61\uff0c\u65e0\u89c6\u65f6\u95f4\u5b57\u7b26\u4e32\u683c\u5f0f\u3002\r\n\r\n```python\r\nfrom nb_time import NbTime\r\nprint(NbTime('20230506T010203.886 +08:00'))\r\nprint(NbTime('2023-05-06 01:02:03.886'))\r\nprint(NbTime('2023-05-06T01:02:03.886 +08:00'))\r\nprint(NbTime('20221206 1:2:3'))\r\nprint(NbTime('Fri Jul 19 06:38:27 2024'))\r\nprint(NbTime('2013-05-05 12:30:45 America/Chicago'))\r\n```\r\n\r\n## 2.5 NbTime \u4f20\u53c2 DateTimeValue\u7c7b\u578b\u5bf9\u8c61\r\n```\r\n>>> from nb_time import DateTimeValue\r\n>>> NbTime(DateTimeValue(year=2022,month=5,day=9,hour=6),time_zone='UTC+7')\r\n<NbTime [2022-05-09 06:00:00 +0700]>\r\n\r\n```\r\n\r\n## 2.6 NbTime\u4f20\u53c2 NbTime\u5bf9\u8c61\r\n\r\nNbTime\u5165\u53c2\u672c\u8eab\u652f\u6301\u65e0\u9650\u5d4c\u5957NbTime\u5bf9\u8c61\r\n```\r\nNbTime(NbTime(NbTime(NbTime())))\r\n<NbTime [2024-02-29 18:39:09]>\r\n\r\n\r\n\u4e3a\u4ec0\u4e48 NbTime\u652f\u6301\u5165\u53c2\u662f\u81ea\u8eab\u7c7b\u578b,\u4f8b\u5982\u4f60\u53ef\u4ee5\u65b9\u4fbf\u7684\u8f6c\u65f6\u533a\u548c\u8f6c\u5b57\u7b26\u4e32\u683c\u5f0f\u5316\r\n\u4f8b\u59820\u65f6\u533a\u76842024-02-29 07:40:34,\u4f60\u8981\u8f6c\u5316\u62108\u65f6\u533a\u7684\u5e26\u6beb\u79d2\u5e26\u65f6\u533a\u7684\u65f6\u95f4\u5b57\u7b26\u4e32,\r\n>>> from nb_time import NbTime \r\n>>> NbTime(NbTime('2024-02-29 07:40:34', time_zone='UTC+0', datetime_formatter=NbTime.FORMATTER_DATETIME_NO_ZONE),\r\n... time_zone='UTC+8', datetime_formatter=NbTime.FORMATTER_MILLISECOND).datetime_str\r\n'2024-02-29 15:40:34.000000 +0800'\r\n```\r\n\r\n## 2.7 NbTime\u4f20\u53c2 arrow.Arrow\u5bf9\u8c61\r\n```\r\n>>> NbTime(arrow.now())\r\n<NbTime [2025-09-09T12:32:58+0800] (Asia/Shanghai)>\r\n```\r\n# 3 NbTime \u94fe\u5f0f\u8ba1\u7b97\u65f6\u95f4\r\n\r\nNbTime().shift\u65b9\u6cd5\u8fd4\u56de\u7684\u5bf9\u8c61\u4ecd\u7136\u662fNbtime\u7c7b\u578b\u3002\r\n\u56e0\u4e3aNbtime\u5bf9\u8c61\u672c\u8eab\u5177\u6709\u5f88\u591a\u597d\u7528\u7684\u5c5e\u6027\u548c\u65b9\u6cd5\uff0c\u6240\u4ee5\u4f7f\u7528NbTime\u4f5c\u4e3a\u65f6\u95f4\u8f6c\u5316\u7684\u4e2d\u8f6c\u5bf9\u8c61\uff0c\u6bd4\u4f7f\u7528datetime\u4f5c\u4e3a\u4e2d\u8f6c\u5bf9\u8c61\u65b9\u4fbf\u4f7f\u7528\u5f88\u591a\u3002\r\n\r\n\r\n\u6c423\u59291\u5c0f\u65f610\u5206\u949f\u540e\u7684\u65f6\u95f4,\u5165\u53c2\u652f\u6301\u6b63\u6570\u548c\u8d1f\u6570\r\n```\r\n>>> NbTime().shift(hours=1,minutes=10).shift(days=3)\r\n<NbTime [2024-03-03 19:02:49 +0800]>\r\n```\r\n\r\n\u6c42\u5f53\u524d\u65f6\u95f41\u5929\u4e4b\u524d\u7684\u65f6\u95f4\u6233\r\n```commandline\r\n>>> NbTime().shift(days=-1).timestamp\r\n1709290123.409756\r\n\r\n```\r\n\r\n`arrow`\u548c`nb_time`\u4e4b\u95f4\u65e0\u9650\u94fe\u5f0f\u8f6c\u5316\r\n```\r\n>>> NbTime().arrow.ceil('day').to_nb_time()\r\n<NbTime [2025-09-09T23:59:59+0800] (UTC+8)>\r\n\r\n```\r\n\r\n\r\n\r\n\r\n\r\n# 3 NbTime \u65f6\u533a\u8bbe\u7f6e\r\n\r\n## 3.1 NbTime \u5b9e\u4f8b\u5316\u65f6\u5019\u8bbe\u7f6e\u65f6\u533a\r\n\r\n\u5b9e\u4f8b\u5316\u65f6\u5019\u5206\u522b\u8bbe\u7f6e\u4e1c7\u533a\u548c0\u65f6\u533a\r\n```\r\n>>> NbTime(time_zone='UTC+7')\r\n<NbTime [2024-02-29 17:05:08 +0700]>\r\n>>> NbTime(time_zone='UTC+0') \r\n<NbTime [2024-02-29 10:05:08 +0000]>\r\n```\r\n\r\n## 3.2 \u5168\u5c40\u8bbe\u7f6e\u65f6\u533a\r\n\u7528\u6237\u4e0d\u4f20\u9012\u65f6\u533a\u65f6\u5019,\u9ed8\u8ba4\u5c31\u662f\u64cd\u4f5c\u7cfb\u7edf\u65f6\u533a,\u5982\u679c\u7528\u6237\u60f3\u7edf\u4e00\u8bbe\u7f6e\u65f6\u533a\r\n\r\n\u4f8b\u5982\u7528\u6237\u7edf\u4e00\u8bbe\u7f6e\u4e1c8\u533a,\u4ee5\u540e\u5b9e\u4f8b\u5316\u5c31\u4e0d\u7528\u6bcf\u6b21\u4eb2\u81ea\u4f20\u9012\u4e1c\u516b\u533a.\r\n```\r\nNbTime.set_default_time_zone('UTC+8')\r\n```\r\n\r\n# 4 \u8bbe\u7f6e\u65f6\u95f4\u5b57\u7b26\u4e32\u683c\u5f0f\u5316\r\n\r\n## 4.1 NBTime\u5b9e\u4f8b\u5316\u65f6\u5019\u8bbe\u7f6e\u65f6\u95f4\u5b57\u7b26\u4e32\u683c\u5f0f\r\n\u7528\u6237\u4e0d\u60f3\u8981\u6beb\u79d2\u65f6\u95f4\u5b57\u7b26\u4e32\r\n```\r\n>>> NbTime(datetime_formatter=NbTime.FORMATTER_DATETIME) \r\n<NbTime [2024-02-29 18:10:57 +0800]>\r\n```\r\n\r\n\u7528\u6237\u4e0d\u60f3\u8981\u5b57\u7b26\u4e32\u5e26\u65f6\u533a\r\n```\r\n>>> NbTime(datetime_formatter=NbTime.FORMATTER_DATETIME_NO_ZONE) \r\n<NbTime [2024-02-29 18:12:18]>\r\n```\r\n\r\n## 4.2 NBTime\u5168\u5c40\u8bbe\u7f6e\u5b57\u7b26\u4e32\u683c\u5f0f\r\n\r\nNbTime.set_default_formatter \u53ef\u4ee5\u5168\u5c40\u8bbe\u7f6e\u65f6\u95f4\u683c\u5f0f\u5b57\u7b26\u4e32,\u5c31\u4e0d\u9700\u8981\u6bcf\u6b21\u90fd\u4f20\u9012\u683c\u5f0f\r\n```\r\n>>> NbTime.set_default_formatter(NbTime.FORMATTER_DATETIME_NO_ZONE)\r\n>>> NbTime()\r\n<NbTime [2024-02-29 18:14:38]>\r\n```\r\n\r\n# 5 NbTime \u5bf9\u8c61\u5185\u7f6e\u7684\u6210\u5458\u5c5e\u6027\r\n\r\n\u89c1\u4e0b\u9762\u7684\u4ea4\u4e92,NbTime\u7c7b\u578b\u5bf9\u8c61\u6709\u975e\u5e38\u4fbf\u6377\u7684\u5404\u79cd\u6210\u5458\u53d8\u91cf,\r\n\r\n```\r\ndatetime \u7c7b\u578bdatetime.datetime\u7c7b\u578b\u7684\u65f6\u95f4\u5bf9\u8c61,\u8fd9\u4e2a\u5f88\u65b9\u4fbf\u548c\u5185\u7f6e\u7c7b\u578b\u5173\u8054\u8d77\u6765\r\ntime_zone_obj \u65f6\u533a\r\ndatetime_str \u65e5\u671f\u65f6\u95f4\u5b57\u7b26\u4e32\r\ntime_str \u65f6\u95f4\u5b57\u7b26\u4e32\r\ndate_str \u65e5\u671f\u5b57\u7b26\u4e32\r\ntimestamp \u65f6\u95f4\u6233\u79d2\r\ntimestamp_millisecond \u65f6\u95f4\u6233\u6beb\u79d2\r\ntoday_zero_timestamp \u5f53\u5929\u51cc\u6668\u7684\u65f6\u95f4\u6233\r\narrow arrow.Arrow\u5bf9\u8c61\r\n```\r\n\r\n```\r\nfrom nb_time import NbTime\r\n>>> nbt=NbTime()\r\n>>> nbt.datetime\r\ndatetime.datetime(2024, 2, 29, 18, 16, 23, 541415, tzinfo=<DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>)\r\n\r\n>>> nbt.time_zone_obj\r\n<DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>\r\n\r\n>>> nbt.datetime_str\r\n'2024-02-29 18:16:23'\r\n\r\n>>> nbt.time_str\r\n'18:16:23'\r\n\r\n>>> nbt.date_str\r\n'2024-02-29'\r\n\r\n>>> nbt.timestamp\r\n1709201783.541415\r\n\r\n>>> nbt.timestamp_millisecond\r\n1709201783541.415\r\n\r\n>>> nbt.today_zero_timestamp\r\n1709136000\r\n\r\n\r\n>>> nbt.arrow\r\n<Arrow [2025-09-09T13:01:17.526580+08:00]>\r\n```\r\n\r\n# 6 NbTime\u7684\u65b9\u6cd5\r\n\r\n## 6.1 get_str \u65b9\u6cd5\u8f6c\u5316\u6210\u4efb\u610f\u5b57\u7b26\u4e32\u683c\u5f0f\r\n```\r\n\u4f8b\u5982\u83b7\u53d6\u4eca\u5929\u7684\u5e74\u6708\u65e5,\u4e2d\u95f4\u4e0d\u8981\u5e26 - \r\n>>> NbTime().get_str('%Y%m%d')\r\n20240301\r\n```\r\n\r\n## 6.2 shift \u662f\u8ba1\u7b97\u751f\u6210\u65b0\u7684NbTime\u5bf9\u8c61,\u652f\u6301\u65e0\u9650\u8fde\u7eed\u94fe\u5f0f\u64cd\u4f5c\r\n```\r\n\u6c423\u59291\u5c0f\u65f610\u5206\u949f\u540e\u7684\u65f6\u95f4,\u5165\u53c2\u652f\u6301\u6b63\u6570\u548c\u8d1f\u6570\r\n>>> NbTime().shift(hours=1,minutes=10).shift(days=3)\r\n<NbTime [2024-03-03 19:02:49 +0800]>\r\n```\r\n\r\n## 6.3 to_tz \u662f\u751f\u6210\u65b0\u7684\u65f6\u533a\u7684NbTime\u5bf9\u8c61,\u628aNbTime\u5bf9\u8c61\u8f6c\u5316\u6210\u53e6\u4e00\u4e2a\u65f6\u533a.\r\n```\r\n\u4e00\u4e2a\u4e1c7\u533a\u7684\u65f6\u95f4:\r\n>>> NbTime('2024-02-26 15:58:21',datetime_formatter=NbTime.FORMATTER_DATETIME,time_zone=NbTime.TIMEZONE_EASTERN_7)\r\n<NbTime [2024-02-26 15:58:21 +0700]>\r\n\r\n\u90a3\u8fd9\u4e2a\u4e1c7\u533a\u7684\u65f6\u95f4\u8f6c\u5316\u6210\u4e1c8\u533a\u7684\u65f6\u95f4:\r\n>>> NbTime('2024-02-26 15:58:21',datetime_formatter=NbTime.FORMATTER_DATETIME,time_zone=NbTime.TIMEZONE_EASTERN_7).to_tz('UTC+8')\r\n<NbTime [2024-02-26 16:58:21 +0800]>\r\n```\r\n\r\n### 6.3.2 \u4e24\u79cd\u65f6\u533a\u8f6c\u5316\u5199\u6cd5\r\n\r\n\u4f8b\u5982\u4e1c7\u533a\u76842024-02-29 07:40:34\u8f6c\u6210\u4e1c\u516b\u533a\u7684\u65f6\u95f4\u5b57\u7b26\u4e32\u3002\r\n\r\n```python\r\nfrom nb_time import NbTime\r\n\r\n# NbTime\u5bf9\u8c61\u65e0\u9650\u5d4c\u5957\u4f20\u53c2\u7ed9NbTime\u65b9\u5f0f\r\nprint(NbTime(NbTime('2024-02-29 07:40:34', time_zone='UTC+7'), time_zone='UTC+8').datetime_str)\r\n\r\n# to_tz \u65b9\u5f0f\r\nprint(NbTime('2024-02-29 07:40:34', time_zone='UTC+7').to_tz('UTC+8').datetime_str)\r\n```\r\n\r\n## 6.4 NbTime \u5bf9\u8c61 \u652f\u6301 > < = \u6bd4\u8f83\r\n```\r\nNbTime \u5b9e\u73b0\u4e86 __gt__ __lt__ __eq__ \u65b9\u6cd5,\u53ef\u4ee5\u76f4\u63a5\u6bd4\u8f83\u5927\u5c0f\r\n\r\n>>> NbTime() > NbTime('2023-05-06 01:01:01') \r\nTrue\r\n>>> NbTime() > NbTime('2025-05-06 01:01:01') \r\nFalse\r\n\r\n```\r\n## 6.5 NbTime \u8f6c\u6362\u4e3a arrow.Arrow\u5bf9\u8c61\r\n\r\n```\r\n>>> nt=NbTime()\r\n>>> nt.to_arrow()\r\n<Arrow [2025-09-09T12:34:37.661360+08:00]>\r\n```\r\n\r\n## 6.6 NbTime humanize \u65b9\u6cd5,\u8f6c\u4eba\u7c7b\u81ea\u7136\u8bed\u8a00\r\n```\r\n>>> NbTime().humanize()\r\n'just now'\r\n\r\n>>> NbTime().shift(days=5).humanize()\r\n'in 5 days'\r\n\r\n>>> NbTime().shift(days=-3).humanize() \r\n'3 days ago'\r\n```\r\n\r\n# 7.\u7528\u6237\u81ea\u5b9a\u4e49\u7ee7\u627f NbTime \u7c7b\r\n\r\n\u56e0\u4e3a nb_time \u662f oop\u9762\u5411\u5bf9\u8c61\u5f00\u53d1\u7684,\u6240\u4ee5\u53ef\u4ee5\u7ee7\u627f,\r\n\u5982\u679c\u662f\u9762\u5411\u8fc7\u7a0b\u7f16\u7a0b,\u4f7f\u7528\u6a21\u5757\u7ea7 + \u51fd\u6570\u7684\u65b9\u5f0f\u6765\u7f16\u7a0b,\u5148\u6539\u53d8\u6a21\u5757\u7684\u67d0\u4e2a\u5168\u5c40\u53d8\u91cf\u6216\u8005\u51fd\u6570\u903b\u8f91,\u53ea\u80fd\u4f7f\u7528\u7334\u5b50\u8865\u4e01\u6280\u672f,\u800c\u4e14\u6a21\u5757\u5929\u7136\u8fd8\u662f\u4e2a\u5355\u4f8b,\u4e0d\u9002\u5408\u591a\u6b21\u7334\u5b50\u8865\u4e01\r\n\u9762\u5411\u5bf9\u8c61\u5c31\u662f\u6709\u4f18\u52bf.\r\n\r\n\r\n## 7.1 \u4f8b\u5982\u7528\u6237\u60f3\u4f7f\u7528 UTC 0\u65f6\u533a,\u4f46\u662f\u4e0d\u60f3\u9891\u7e41\u4f20\u9012 \u65f6\u533a\u5165\u53c2,\u53ef\u4ee5\u4f7f\u7528 nb_time\u7684 \u81ea\u5e26\u7684UtcNbTime \u7c7b,\u6216\u8005\u7528\u6237\u624b\u5199\u8fd9\u4e2a\u7c7b\u81ea\u5df1\u7ee7\u627fNbTime\r\n\r\n```python\r\nclass UtcNbTime(NbTime):\r\n default_time_zone = NbTime.TIMEZONE_UTC\r\n\r\n# \u4f7f\u7528\u7684\u65f6\u5019\r\nUtcNbTime() \r\n```\r\n\r\n\r\n## 7.2 \u4f8b\u5982 \u7528\u6237\u60f3\u4f7f\u7528\u4e0a\u6d77\u65f6\u533a,\u5e76\u4e14\u9ed8\u8ba4\u4f7f\u7528\u4e0d\u5e26\u65f6\u533a\u7684\u65f6\u95f4\u5b57\u7b26\u4e32\u683c\u5f0f\u5316\r\n```python\r\nclass ShanghaiNbTime(NbTime):\r\n default_time_zone = NbTime.TIMEZONE_ASIA_SHANGHAI\r\n default_formatter = NbTime.FORMATTER_DATETIME_NO_ZONE\r\n\r\n# \u4f7f\u7528\u7684\u65f6\u5019\r\nShanghaiNbTime() \r\n```\r\n\r\n## 7.3 \u6570\u636e\u5206\u6790,\u5e38\u7528\u7684\u65f6\u95f4\u4e5f\u53ef\u4ee5\u52a0\u4e0a\u6765\r\n\r\n```python\r\nclass PopularNbTime(NbTime):\r\n @property\r\n def ago_1_days(self):\r\n return self.shift(days=-1)\r\n\r\n @property\r\n def ago_7_days(self):\r\n return self.shift(days=-7)\r\n\r\n @property\r\n def ago_30_days(self):\r\n return self.shift(days=-30)\r\n\r\n @property\r\n def ago_180_days(self):\r\n return self.shift(days=-180)\r\n```\r\n\r\n# 8 nb_time \u6027\u80fd\u66b4\u6253 arrow 700%\r\n```python\r\nfor i in range(1000000):\r\n NbTime(time_zone='Asia/Shanghai') # 3\u79d2100\u4e07\u6b21\r\n # arrow.now(tz='Asia/Shanghai') # 20\u79d2100\u4e07\u6b21\r\n```\r\n\r\n# 9 \u6f14\u793a\u6700\u5dee\u52b2\u7684 utils/time_utils.py\r\n\r\n**\u4e00\u4e2a\u201c\u6559\u79d1\u4e66\u7ea7\u5c4e\u5c71\u65f6\u95f4\u5de5\u5177\u5305\u201d** \r\n\r\n\u274c \u6240\u6709\u51fd\u6570\u90fd\u662f\u9762\u5411\u8fc7\u7a0b\u3001\u65e0\u5c01\u88c5\u3001\u65e0\u94fe\u5f0f \r\n\u274c \u6bcf\u4e2a\u51fd\u6570\u53ea\u505a\u201c\u534a\u4ef6\u4e8b\u201d\uff0c\u7528\u6237\u5fc5\u987b\u7ec4\u5408 3~4 \u4e2a\u51fd\u6570\u624d\u80fd\u5b8c\u6210\u4e00\u4e2a\u8f6c\u6362 \r\n\u274c \u53c2\u6570\u547d\u540d\u6a21\u7cca\u3001\u683c\u5f0f\u4e0d\u7edf\u4e00\u3001\u6587\u6863\u7f3a\u5931 \r\n\u274c \u65f6\u533a\u5904\u7406\u9760\u201c\u731c\u201d\u548c\u201c\u786c\u7f16\u7801\u6570\u5b57\u201d \r\n\u274c \u540c\u4e00\u4e2a\u529f\u80fd\u6709 5 \u4e2a\u540d\u5b57\u7c7b\u4f3c\u7684\u51fd\u6570\uff0c\u884c\u4e3a\u7565\u6709\u4e0d\u540c \r\n\u274c \u4e0d\u5904\u7406\u5f02\u5e38\u3001\u4e0d\u6821\u9a8c\u8f93\u5165\u3001\u51fa\u9519\u9759\u9ed8\u6216\u5d29\u6e83 \r\n\u274c \u5927\u91cf\u91cd\u590d\u4ee3\u7801\u3001\u9b54\u6cd5\u6570\u5b57\u3001\u5168\u5c40\u53d8\u91cf\u6c61\u67d3 \r\n\u274c \u4f9d\u8d56\u7cfb\u7edf\u672c\u5730\u65f6\u533a\uff0c\u8de8\u73af\u5883\u884c\u4e3a\u4e0d\u4e00\u81f4 \r\n\u274c \u652f\u6301\u201c\u591a\u79cd\u683c\u5f0f\u201d\uff0c\u4f46\u7528\u6237\u5f97\u81ea\u5df1\u67e5\u6e90\u7801\u624d\u77e5\u9053\u652f\u6301\u54ea\u4e9b \r\n\r\n\ud83d\udca3 \u6700\u5dee\u52b2 time_utils.py \u51fa\u7089\uff01 \r\n```python\r\n# time_utils.py \u2014\u2014 \u5730\u72f1\u7ea7\u65f6\u95f4\u5de5\u5177\u5305\r\n# \u4f5c\u8005\uff1a\u65f6\u95f4\u6df7\u4e71\u4e4b\u795e\r\n# \u8b66\u544a\uff1a\u4f7f\u7528\u672c\u6587\u4ef6\u53ef\u80fd\u5bfc\u81f4\u8131\u53d1\u3001\u5931\u7720\u3001\u79bb\u804c\r\n\r\nimport time\r\nfrom datetime import datetime, timedelta\r\n\r\n# \u5168\u5c40\u53d8\u91cf\uff0c\u5047\u88c5\u662f\u914d\u7f6e\uff0c\u5176\u5b9e\u662f\u9690\u85cf\u70b8\u5f39\r\nDEFAULT_TIME_FORMAT = \"%Y-%m-%d %H:%M:%S\"\r\nSYSTEM_TZ_OFFSET = -time.timezone // 3600 # \u4f9d\u8d56\u8fd0\u884c\u73af\u5883\uff01\u4e1c8\u533a\u670d\u52a1\u5668=8\uff0c\u6d1b\u6749\u77f6=-7\r\n\r\n# =================== \u5b57\u7b26\u4e32\u89e3\u6790\u533a\uff08\u6bcf\u4e2a\u51fd\u6570\u53ea\u89e3\u6790\u4e00\u79cd\u683c\u5f0f\uff09 ===================\r\n\r\ndef parse_time_str_type1(s):\r\n # \u53ea\u652f\u6301 \"2025-04-05 14:30:00\"\r\n return time.strptime(s, \"%Y-%m-%d %H:%M:%S\")\r\n\r\ndef parse_time_str_type2(s):\r\n # \u53ea\u652f\u6301 \"2025/04/05 14:30\"\r\n return time.strptime(s, \"%Y/%m/%d %H:%M\")\r\n\r\ndef parse_time_str_type3(s):\r\n # \u53ea\u652f\u6301 \"04-05-2025\"\r\n return time.strptime(s, \"%m-%d-%Y\")\r\n\r\ndef parse_time_str_universal(s):\r\n # \u201c\u901a\u7528\u201d\u4f46\u53ea try 3 \u79cd\uff0c\u5931\u8d25\u5c31\u5d29\u6e83\r\n for fmt in [\"%Y-%m-%d\", \"%d/%m/%Y %H:%M\", \"%Y\u5e74%m\u6708%d\u65e5\"]:\r\n try:\r\n return time.strptime(s, fmt)\r\n except:\r\n continue\r\n raise Exception(\"\u8001\u5b50\u89e3\u6790\u4e0d\u4e86\uff0c\u4f60\u81ea\u5df1\u770b\u7740\u529e\")\r\n\r\n# =================== \u65f6\u95f4\u7ed3\u6784\u4f53 \u2192 \u65f6\u95f4\u6233 \uff08\u4f46\u6709\u65f6\u533a\u5751\uff09 ===================\r\n\r\ndef struct_time_to_timestamp_utc(st):\r\n # \u8f93\u5165 struct_time\uff0c\u8fd4\u56de UTC \u65f6\u95f4\u6233\uff1f\u4e0d\uff01\u662f\u672c\u5730\u65f6\u95f4\u6233\uff01\r\n return int(time.mktime(st))\r\n\r\ndef struct_time_to_timestamp_assume_utc(st):\r\n # \u5047\u88c5 st \u662f UTC\uff0c\u4f46 mktime \u662f\u672c\u5730\u2026\u2026\u6240\u4ee5\u9519\u4e86\r\n return int(time.mktime(st)) - SYSTEM_TZ_OFFSET * 3600\r\n\r\ndef struct_time_to_timestamp_with_offset(st, offset_hours):\r\n # offset_hours \u662f\u4ec0\u4e48\uff1f\u6b63\u8d1f\uff1f\u6587\u6863\uff1f\u4e0d\u5b58\u5728\u7684\r\n return int(time.mktime(st)) - offset_hours * 3600\r\n\r\n# =================== \u65f6\u95f4\u6233 \u2192 struct_time ===================\r\n\r\ndef timestamp_to_struct_local(ts):\r\n return time.localtime(ts)\r\n\r\ndef timestamp_to_struct_utc(ts):\r\n return time.gmtime(ts)\r\n\r\n# =================== struct_time \u2192 \u5b57\u7b26\u4e32 ===================\r\n\r\ndef struct_time_to_str_format1(st):\r\n return time.strftime(\"%Y-%m-%d %H:%M:%S\", st)\r\n\r\ndef struct_time_to_str_format2(st):\r\n return time.strftime(\"%Y/%m/%d %H\u70b9%M\u5206\", st)\r\n\r\ndef struct_time_to_str_custom(st, fmt):\r\n return time.strftime(fmt, st)\r\n\r\n# =================== \u65f6\u533a\u8f6c\u6362\uff08\u9760\u731c\uff09 ===================\r\n\r\ndef convert_timestamp_from_tz7_to_tz8(ts):\r\n # \u786c\u7f16\u7801 +3600\uff0c\u5047\u88c5\u4e13\u4e1a\r\n return ts + 3600\r\n\r\ndef convert_timestamp_by_offset_diff(ts, from_offset, to_offset):\r\n # \u6587\u6863\uff1f\u53c2\u6570\u987a\u5e8f\uff1f\u8c01\u8bb0\u5f97\r\n return ts + (to_offset - from_offset) * 3600\r\n\r\ndef convert_string_timezone_manual(s, from_tz_num, to_tz_num):\r\n # \u7528\u6237\u5fc5\u987b\u81ea\u5df1\uff1a1.\u9009\u89e3\u6790\u51fd\u6570 2.\u8f6c\u65f6\u95f4\u6233 3.\u8c03\u6b64\u51fd\u6570 4.\u518d\u8f6c\u56de\u5b57\u7b26\u4e32\r\n st = parse_time_str_type1(s) # \u5047\u8bbe\u683c\u5f0f\u5bf9\r\n ts = struct_time_to_timestamp_utc(st)\r\n ts2 = convert_timestamp_by_offset_diff(ts, from_tz_num, to_tz_num)\r\n st2 = timestamp_to_struct_local(ts2)\r\n return struct_time_to_str_format1(st2)\r\n\r\n# =================== \u201c\u4fbf\u6377\u201d\u51fd\u6570\uff08\u5176\u5b9e\u66f4\u9ebb\u70e6\uff09 ===================\r\n\r\ndef get_current_time_in_tz8_str():\r\n # \u8fd4\u56de\u201c\u5f53\u524d\u4e1c\u516b\u533a\u65f6\u95f4\u5b57\u7b26\u4e32\u201d\uff0c\u4f46\u4f9d\u8d56\u670d\u52a1\u5668\u65f6\u533a\uff01\r\n return time.strftime(\"%Y-%m-%d %H:%M:%S\", time.localtime())\r\n\r\ndef get_current_timestamp_in_utc():\r\n return int(time.time())\r\n\r\ndef add_days_to_timestamp(ts, days):\r\n return ts + days * 86400\r\n\r\ndef add_hours_to_struct_time(st, hours):\r\n # struct_time \u662f\u53ea\u8bfb\u7684\uff01\u5fc5\u987b\u8f6c\u6210 datetime \u624d\u80fd\u52a0\uff01\u4f46\u7528\u6237\u4e0d\u77e5\u9053\uff01\r\n dt = datetime(*st[:6])\r\n dt2 = dt + timedelta(hours=hours)\r\n return dt2.timetuple() # \u8fd4\u56de struct_time\uff0c\u4f46\u4e22\u5931\u5fae\u79d2\u548c\u65f6\u533a\uff01\r\n\r\n# =================== \u9690\u85cf\u9677\u9631\u51fd\u6570 ===================\r\n\r\ndef quick_convert(s):\r\n # \u540d\u5b57\u5f88\u8bf1\u4eba\uff0c\u884c\u4e3a\u5f88\u968f\u673a\r\n if \"-\" in s:\r\n st = parse_time_str_type1(s)\r\n elif \"/\" in s:\r\n st = parse_time_str_type2(s)\r\n else:\r\n st = parse_time_str_universal(s)\r\n ts = struct_time_to_timestamp_utc(st)\r\n ts += 3600 # \u5047\u88c5\u8f6c\u4e1c8\u533a\r\n return time.strftime(\"%Y-%m-%d %H:%M\", time.localtime(ts))\r\n\r\n# =================== \u591a\u4f59\u51fd\u6570\uff08\u5236\u9020\u9009\u62e9\u56f0\u96be\uff09 ===================\r\n\r\ndef str_to_timestamp_method_a(s): return struct_time_to_timestamp_utc(parse_time_str_type1(s))\r\ndef str_to_timestamp_method_b(s): return struct_time_to_timestamp_assume_utc(parse_time_str_type1(s))\r\ndef str_to_timestamp_method_c(s): return struct_time_to_timestamp_with_offset(parse_time_str_type1(s), 8)\r\n\r\n# =================== \u5f69\u86cb\uff1a\u5168\u5c40\u72b6\u6001\u6c61\u67d3 ===================\r\n\r\n_last_parsed_time = None # \u6240\u6709\u51fd\u6570\u5077\u5077\u4fee\u6539\u5b83\uff0c\u7528\u4e8e\u201c\u8c03\u8bd5\u201d\r\n\r\ndef parse_and_remember(s):\r\n global _last_parsed_time\r\n st = parse_time_str_type1(s)\r\n _last_parsed_time = st\r\n return st\r\n\r\n# =================== \u6587\u6863\uff1f\u4e0d\u5b58\u5728\u7684 ===================\r\n\r\n# \u6ca1\u6709\u7c7b\u578b\u63d0\u793a\r\n# \u6ca1\u6709 docstring\r\n# \u6ca1\u6709\u793a\u4f8b\r\n# \u6ca1\u6709\u6d4b\u8bd5\r\n# \u53ea\u6709\u6ce8\u91ca\uff1a\u201c\u4ee5\u540e\u518d\u6539\u201d\u3001\u201c\u4e34\u65f6\u65b9\u6848\u201d\u3001\u201c\u522b\u52a8\u8fd9\u4e2a\uff01\uff01\uff01\u201d\r\n```\r\n\r\n\ud83e\udde9\u7528\u6237\u4f7f\u7528\u793a\u4f8b\uff08\u4e1c7\u533a\u65f6\u95f4\u5b57\u7b26\u4e32 \u2192 \u4e1c8\u533a\u65f6\u95f4\u5b57\u7b26\u4e32): \r\n\r\n```python\r\nfrom time_utils import *\r\n\r\ninput_str = \"2025-04-05 14:30:00\"\r\n\r\n# Step 1: \u89e3\u6790\u5b57\u7b26\u4e32 \u2192 struct_time\r\nst = parse_time_str_type1(input_str) # \u5982\u679c\u683c\u5f0f\u4e0d\u5bf9\uff1f\u5d29\u6e83\uff01\r\n\r\n# Step 2: struct_time \u2192 \u65f6\u95f4\u6233\uff08\u4f46\u8fd9\u662f\u672c\u5730\u65f6\u95f4\u6233\uff01\u5751\uff01\uff09\r\nts = struct_time_to_timestamp_utc(st)\r\n\r\n# Step 3: \u624b\u52a8\u52a03600\u79d2\uff0c\u5047\u88c5\u65f6\u533a\u8f6c\u6362\r\nts_east8 = ts + 3600\r\n\r\n# Step 4: \u65f6\u95f4\u6233 \u2192 struct_time\uff08\u672c\u5730\u65f6\u533a\uff01\u518d\u6b21\u4f9d\u8d56\u670d\u52a1\u5668\uff01\uff09\r\nst_east8 = timestamp_to_struct_local(ts_east8)\r\n\r\n# Step 5: struct_time \u2192 \u5b57\u7b26\u4e32\r\noutput_str = struct_time_to_str_format1(st_east8)\r\n\r\nprint(output_str) # \u53ef\u80fd\u662f \"2025-04-05 15:30:00\" \u2014\u2014 \u5982\u679c\u670d\u52a1\u5668\u5728\u4e1c8\u533a\uff01\r\n # \u5982\u679c\u5728\u6d1b\u6749\u77f6\uff1f\u2192 \u8f93\u51fa\u9519\u8bef\u65f6\u95f4\uff01\r\n```\r\n\r\n\r\n# 10 NbTime\u603b\u7ed3\r\n```\r\n\u603b\u7ed3\u5c31\u662f NbTime \u7684\u5165\u53c2\u63a5\u53d7\u6240\u6709\u7c7b\u578b,NbTime\u652f\u6301\u94fe\u5f0f\u8c03\u7528,Nbtime\u65b9\u4fbf\u652f\u6301\u65f6\u533a,Nbtime\u65b9\u4fbf\u64cd\u4f5c\u65f6\u95f4\u8f6c\u5316,\r\n\u6240\u4ee5NbTime\u64cd\u4f5c\u65f6\u95f4,\u8fdc\u8fdc\u66b4\u51fb\u4f7f\u7528datetime\u548c\u4e09\u65b9arrow\u5305,\r\n\u8fdc\u8fdc\u66b4\u51fb\u7528\u6237\u5728 utils.time_utils.py\u6587\u4ef6\u4e2d\u5199\u51e0\u767e\u4e2a\u5b64\u7acb\u7684\u9762\u5411\u8fc7\u7a0b\u64cd\u4f5c\u65f6\u95f4\u7684\u51fd\u6570.\r\n```\r\n",
"bugtrack_url": null,
"license": "BSD License",
"summary": "Awesome time conversion handling with support for chaining operations.",
"version": "2.6",
"project_urls": {
"Homepage": "https://github.com/ydf0509/nb_time"
},
"split_keywords": [
"arrow",
" time",
" datetime",
" time_utils"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "36e340fcc72d5e930d4882479e84a0f4e41c29aa9cecbe415a969bb4d5449c5c",
"md5": "2fbbcb91071bc0c1ead595a685b42960",
"sha256": "91c68bd486baaeaf32ca5775bad82b46dd5c788bacf403d0d21f73869cf23178"
},
"downloads": -1,
"filename": "nb_time-2.6-py3-none-any.whl",
"has_sig": false,
"md5_digest": "2fbbcb91071bc0c1ead595a685b42960",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 16203,
"upload_time": "2025-09-09T07:16:03",
"upload_time_iso_8601": "2025-09-09T07:16:03.823711Z",
"url": "https://files.pythonhosted.org/packages/36/e3/40fcc72d5e930d4882479e84a0f4e41c29aa9cecbe415a969bb4d5449c5c/nb_time-2.6-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "323b0e9e0150f0f5cf41504247392d032b5f7114d4ef2a4ce1d976b1498942b1",
"md5": "9728c59a3a95450d4bfcbeb75888c3c4",
"sha256": "d5d6da675af67d7f78acc9f0de2ff7252f83ab6607d10eb8b16e5788324606a3"
},
"downloads": -1,
"filename": "nb_time-2.6.tar.gz",
"has_sig": false,
"md5_digest": "9728c59a3a95450d4bfcbeb75888c3c4",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 24024,
"upload_time": "2025-09-09T07:16:05",
"upload_time_iso_8601": "2025-09-09T07:16:05.075506Z",
"url": "https://files.pythonhosted.org/packages/32/3b/0e9e0150f0f5cf41504247392d032b5f7114d4ef2a4ce1d976b1498942b1/nb_time-2.6.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-09-09 07:16:05",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ydf0509",
"github_project": "nb_time",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "nb-time"
}