155 lines
4.4 KiB
Python
155 lines
4.4 KiB
Python
import hashlib
|
||
import binascii
|
||
import logging
|
||
from datetime import datetime, timezone
|
||
import os
|
||
|
||
def format_phone(
|
||
phone: str,
|
||
groups: list[int] | None = None,
|
||
separator: str = " ",
|
||
private: bool = False
|
||
) -> str:
|
||
"""
|
||
格式化中国大陆的11位手机号
|
||
|
||
:param phone: 手机号
|
||
:param groups: 分组长度列表
|
||
:param separator: 分隔符
|
||
:param private: 是否隐藏前七位
|
||
|
||
:return: 格式化后的手机号
|
||
"""
|
||
if groups is None:
|
||
groups = [3, 4, 4]
|
||
|
||
result = []
|
||
start = 0
|
||
for i, length in enumerate(groups):
|
||
segment = phone[start:start + length]
|
||
# 如果是private模式,将前两组(前7位)替换为星号
|
||
if private and i < 2:
|
||
segment = "*" * length
|
||
result.append(segment)
|
||
start += length
|
||
|
||
return separator.join(result)
|
||
|
||
def generate_password(
|
||
length: int = 8
|
||
) -> str:
|
||
"""
|
||
生成指定长度的随机密码。
|
||
|
||
:param length: 密码长度
|
||
:type length: int
|
||
:return: 随机密码
|
||
:rtype: str
|
||
"""
|
||
import secrets
|
||
|
||
return secrets.token_hex(length)
|
||
|
||
def hash_password(
|
||
password: str
|
||
) -> str:
|
||
"""
|
||
生成密码的加盐哈希值。
|
||
|
||
:param password: 需要哈希的原始密码
|
||
:type password: str
|
||
:return: 包含盐值和哈希值的字符串
|
||
:rtype: str
|
||
|
||
使用SHA-256和PBKDF2算法对密码进行加盐哈希,返回盐值和哈希值的组合。
|
||
"""
|
||
salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
|
||
pwdhash = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt, 100000)
|
||
pwdhash = binascii.hexlify(pwdhash)
|
||
return (salt + pwdhash).decode('ascii')
|
||
|
||
def verify_password(
|
||
stored_password: str,
|
||
provided_password: str,
|
||
debug: bool = False
|
||
) -> bool:
|
||
"""
|
||
验证存储的密码哈希值与用户提供的密码是否匹配。
|
||
|
||
:param stored_password: 存储的密码哈希值(包含盐值)
|
||
:type stored_password: str
|
||
:param provided_password: 用户提供的密码
|
||
:type provided_password: str
|
||
:param debug: 是否输出调试信息,将会输出原密码和哈希值
|
||
:type debug: bool
|
||
:return: 如果密码匹配返回True,否则返回False
|
||
:rtype: bool
|
||
|
||
从存储的密码哈希中提取盐值,使用相同的哈希算法验证用户提供的密码。
|
||
"""
|
||
salt = stored_password[:64]
|
||
stored_password = stored_password[64:]
|
||
pwdhash = hashlib.pbkdf2_hmac('sha256',
|
||
provided_password.encode('utf-8'),
|
||
salt.encode('ascii'),
|
||
100000)
|
||
pwdhash = binascii.hexlify(pwdhash).decode('ascii')
|
||
if debug:
|
||
logging.info(f"原密码: {provided_password}, 哈希值: {pwdhash}, 存储哈希值: {stored_password}")
|
||
return pwdhash == stored_password
|
||
|
||
def format_time_diff(
|
||
target_time: datetime | str
|
||
) -> str:
|
||
"""
|
||
计算目标时间与当前时间的差值,返回易读的中文描述
|
||
|
||
Args:
|
||
target_time: 目标时间,可以是datetime对象或时间字符串
|
||
|
||
Returns:
|
||
str: 格式化的时间差描述,如"一年前"、"3个月前"等
|
||
"""
|
||
# 如果输入是字符串,先转换为datetime对象
|
||
if isinstance(target_time, str):
|
||
try:
|
||
target_time = datetime.fromisoformat(target_time)
|
||
except ValueError:
|
||
return "时间格式错误"
|
||
|
||
now = datetime.now(timezone.utc)
|
||
target_time = target_time.astimezone(timezone.utc)
|
||
diff = now - target_time
|
||
|
||
# 如果是未来时间
|
||
if diff.total_seconds() < 0:
|
||
diff = -diff
|
||
suffix = "后"
|
||
else:
|
||
suffix = "前"
|
||
|
||
seconds = diff.total_seconds()
|
||
|
||
# 定义时间间隔
|
||
intervals = [
|
||
(31536000, " 年"),
|
||
(2592000, " 个月"),
|
||
(86400, " 天"),
|
||
(3600, " 小时"),
|
||
(60, " 分钟"),
|
||
(1, " 秒")
|
||
]
|
||
|
||
# 计算最适合的时间单位
|
||
for seconds_in_unit, unit in intervals:
|
||
if seconds >= seconds_in_unit:
|
||
value = int(seconds / seconds_in_unit)
|
||
if unit == "个月" and value >= 12: # 超过12个月显示为年
|
||
continue
|
||
return f"{value}{unit}{suffix}"
|
||
|
||
return f"刚刚"
|
||
|
||
if __name__ == "__main__":
|
||
print(format_phone("18888888888", private=True))
|
||
print(format_phone("18888888888", private=False)) |