@clawhub-darbling-6ea1e41493
Convert Chinese text to Pinyin (拼音). 中文转拼音工具,支持声调标记、去声调、首字母大写。适合语言学习、输入法开发、中文处理。Chinese to Pinyin converter with tone marks.
---
name: Pinyin Converter
description: "Convert Chinese text to Pinyin (拼音). 中文转拼音工具,支持声调标记、去声调、首字母大写。适合语言学习、输入法开发、中文处理。Chinese to Pinyin converter with tone marks."
tags: pinyin, chinese, converter, tone, language, 拼音, mandarin, utility, tool
---
# Pinyin Converter 🔤
中文转拼音工具。
## Features | 功能
- **标准拼音**:带声调标记
- **无声音调**:纯字母拼音
- **首字母**:仅保留首字母缩写
## Usage | 使用
```bash
# 标准拼音(带声调)
python3 scripts/pinyin.py "你好世界"
# nǐ hǎo shì jiè
# 无声音调
python3 scripts/pinyin.py "你好世界" --no-tone
# ni hao shi jie
# 首字母
python3 scripts/pinyin.py "你好世界" --initial
# NHSJ
```
---
*免责声明:本工具仅供学习参考,不构成任何投资或商业建议。*
FILE:scripts/pinyin.py
#!/usr/bin/env python3
"""Chinese to Pinyin Converter"""
import sys
import json
# Pinyin database (simplified - common characters)
PINYIN_MAP = {
'的': 'de', '一': 'yī', '是': 'shì', '了': 'le', '在': 'zài', '不': 'bù', '有': 'yǒu',
'人': 'rén', '这': 'zhè', '中': 'zhōng', '大': 'dà', '为': 'wéi', '上': 'shàng', '个': 'gè',
'国': 'guó', '我': 'wǒ', '以': 'yǐ', '要': 'yào', '他': 'tā', '时': 'shí', '来': 'lái',
'用': 'yòng', '们': 'men', '生': 'shēng', '到': 'dào', '作': 'zuò', '地': 'dì', '于': 'yú',
'出': 'chū', '就': 'jiù', '分': 'fēn', '对': 'duì', '成': 'chéng', '会': 'huì', '可': 'kě',
'主': 'zhǔ', '发': 'fā', '年': 'nián', '动': 'dòng', '同': 'tóng', '工': 'gōng', '也': 'yě',
'能': 'néng', '下': 'xià', '过': 'guò', '子': 'zǐ', '说': 'shuō', '产': 'chǎn', '种': 'zhǒng',
'面': 'miàn', '而': 'ér', '方': 'fāng', '后': 'hòu', '多': 'duō', '定': 'dìng', '行': 'xíng',
'学': 'xué', '所': 'suǒ', '民': 'mín', '得': 'dé', '经': 'jīng', '十': 'shí', '三': 'sān',
'之': 'zhī', '进': 'jìn', '着': 'zhe', '等': 'děng', '部': 'bù', '度': 'dù', '家': 'jiā',
'里': 'lǐ', '新': 'xīn', '力': 'lì', '请': 'qǐng', '联': 'lián', '合': 'hé', '机': 'jī',
'无': 'wú', '心': 'xīn', '量': 'liàng', '多': 'duō', '么': 'me', '事': 'shì', '知': 'zhī',
'间': 'jiān', '去': 'qù', '什': 'shén', '么': 'me', '还': 'hái', '天': 'tiān', '日': 'rì',
'本': 'běn', '月': 'yuè', '年': 'nián', '好': 'hǎo', '小': 'xiǎo', '伙': 'huǒ', '伴': 'bàn',
'你': 'nǐ', '好': 'hǎo', '世': 'shì', '界': 'jiè', '北': 'běi', '京': 'jīng', '上': 'shàng',
'海': 'hǎi', '深': 'shēn', '圳': 'zhèn', '广': 'guǎng', '州': 'zhōu', '杭': 'háng', '州': 'zhōu',
'成': 'chéng', '都': 'dōu', '重': 'chóng', '庆': 'qìng', '天': 'tiān', '津': 'jīn', '南': 'nán',
'京': 'jīng', '西': 'xī', '安': 'ān', '武': 'wǔ', '汉': 'hàn', '长': 'cháng', '沙': 'shā',
'郑': 'zhèng', '州': 'zhōu', '沈': 'shěn', '阳': 'yáng', '哈': 'hā', '尔': 'ěr', '滨': 'bīn',
}
TONE_MAP = {
'a': 'ā á ǎ à', 'e': 'ē é ě è', 'i': 'ī í ǐ ì', 'o': 'ō ó ǒ ò', 'u': 'ū ú ǔ ù',
'v': 'ǖ ǘ ǚ ǜ', 'ü': 'ǖ ǘ ǚ ǜ'
}
def strip_tones(py):
for k, v in TONE_MAP.items():
tones = v.split()
for t in tones[1:]:
py = py.replace(t, k)
return py
def convert(text, no_tone=False, initial_only=False):
"""Convert Chinese text to pinyin"""
result = []
for char in text:
if char in PINYIN_MAP:
py = PINYIN_MAP[char]
if no_tone:
py = strip_tones(py)
if initial_only:
py = py[0].upper() if py else ''
result.append(py)
elif char.isascii() and not char.isspace():
result.append(char)
elif char in ',.?!,。?!、;;::':
result.append(char)
# skip whitespace
return ' '.join(result)
def main():
args = sys.argv[1:]
no_tone = '--no-tone' in args
initial = '--initial' in args
text = ' '.join([a for a in args if not a.startswith('--')])
if not text:
print(json.dumps({
"usage": "python3 pinyin.py <中文文本> [--no-tone] [--initial]",
"examples": [
"python3 pinyin.py 你好世界",
"python3 pinyin.py 你好世界 --no-tone",
"python3 pinyin.py 北京上海 --initial"
]
}, ensure_ascii=False, indent=2))
return
result = convert(text, no_tone=no_tone, initial_only=initial)
mode = "首字母" if initial else ("无声音调" if no_tone else "标准拼音")
print(json.dumps({"input": text, "pinyin": result, "mode": mode}, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
Validate and extract info from Chinese ID card numbers (身份证). 身份证号码验证、归属地查询、出生日期提取、性别判断、年龄计算、15位转18位。China mainland ID card validator and parser.
---
name: China ID Validator
description: "Validate and extract info from Chinese ID card numbers (身份证). 身份证号码验证、归属地查询、出生日期提取、性别判断、年龄计算、15位转18位。China mainland ID card validator and parser."
tags: china, id, card, validator, identity, 身份证, chinese, utility, tool
---
# China ID Validator 🪪
中国居民身份证号码验证与信息提取工具。
## Features | 功能
- **号码验证**:15位/18位身份证合法性校验
- **信息提取**:省份、出生日期、性别、年龄
- **格式转换**:15位↔18位互转
- **校验码验证**:18位末位校验位验证
## Usage | 使用
```bash
# 验证身份证号
python3 scripts/id_validator.py 110101199003079
# 提取信息
python3 scripts/id_validator.py validate 110101199003079
# 生成测试号码(仅供测试)
python3 scripts/id_validator.py generate 11 1990 3 7 男
```
---
*免责声明:本工具仅供学习参考,不构成任何投资或商业建议。*
FILE:scripts/id_validator.py
#!/usr/bin/env python3
"""Chinese ID Card (身份证) Validator & Info Extractor"""
import sys
import json
import re
from datetime import datetime
PROVINCE_CODES = {
"11":"北京","12":"天津","13":"河北","14":"山西","15":"内蒙古",
"21":"辽宁","22":"吉林","23":"黑龙江","31":"上海","32":"江苏",
"33":"浙江","34":"安徽","35":"福建","36":"江西","37":"山东",
"41":"河南","42":"湖北","43":"湖南","44":"广东","45":"广西",
"46":"海南","50":"重庆","51":"四川","52":"贵州","53":"云南",
"54":"西藏","61":"陕西","62":"甘肃","63":"青海","64":"宁夏",
"65":"新疆","71":"台湾","81":"香港","82":"澳门","91":"国外"
}
WEIGHTS = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
CHECK_CHARS = "10X98765432"
def validate(id_number):
"""Validate a Chinese ID card number (15 or 18 digits)"""
id_number = id_number.strip().upper()
if re.match(r'^\d{15}$', id_number):
return validate_15(id_number)
elif re.match(r'^\d{17}[\dX]$', id_number):
return validate_18(id_number)
else:
return {"valid": False, "error": "格式错误:应为15位纯数字或18位数字+X"}
def validate_15(id_number):
province = id_number[:2]
if province not in PROVINCE_CODES:
return {"valid": False, "error": f"无效省份代码: {province}"}
try:
datetime.strptime("19" + id_number[6:12], "%Y%m%d")
except ValueError:
return {"valid": False, "error": "无效出生日期"}
return {
"valid": True,
"type": "15位",
"province": PROVINCE_CODES[province],
"province_code": province,
"birthday": "19" + id_number[6:12],
"gender": "女" if int(id_number[14]) % 2 == 0 else "男",
"converted_18": convert_15_to_18(id_number)
}
def validate_18(id_number):
province = id_number[:2]
if province not in PROVINCE_CODES:
return {"valid": False, "error": f"无效省份代码: {province}"}
try:
birth = datetime.strptime(id_number[6:14], "%Y%m%d")
except ValueError:
return {"valid": False, "error": "无效出生日期"}
# Check checksum
total = sum(int(id_number[i]) * WEIGHTS[i] for i in range(17))
check = CHECK_CHARS[total % 11]
if check != id_number[17]:
return {"valid": False, "error": f"校验码错误:末位应为{check},实际为{id_number[17]}"}
age = (datetime.now() - birth).days // 365
return {
"valid": True,
"type": "18位",
"province": PROVINCE_CODES[province],
"province_code": province,
"birthday": id_number[6:14],
"age": age,
"gender": "女" if int(id_number[16]) % 2 == 0 else "男",
"checksum": id_number[17]
}
def convert_15_to_18(id15):
"""Convert 15-digit ID to 18-digit"""
id17 = "19" + id15[:6] + id15[6:]
total = sum(int(id17[i]) * WEIGHTS[i] for i in range(17))
return id17 + CHECK_CHARS[total % 11]
def generate(province_code, year, month, day, gender):
"""Generate a random valid ID number (for testing only)"""
import random
if province_code not in PROVINCE_CODES:
return {"error": f"无效省份代码: {province_code}"}
date_str = f"{year}{month.zfill(2)}{day.zfill(2)}"
try:
datetime.strptime(date_str, "%Y%m%d")
except ValueError:
return {"error": "无效日期"}
city_county = f"{random.randint(1,99):02d}{random.randint(1,99):02d}"
region = province_code + city_county[:4] # 6-digit region code
seq = random.randint(10, 99)
gender_digit = random.choice([d for d in range(10) if d % 2 == (0 if gender == "女" else 1)])
id17 = region + date_str + f"{seq}{gender_digit}"
total = sum(int(id17[i]) * WEIGHTS[i] for i in range(17))
return {"id_number": id17 + CHECK_CHARS[total % 11], "note": "仅供测试使用"}
def main():
if len(sys.argv) < 2:
print(json.dumps({"error": "用法: id_validator.py <身份证号|validate|generate>", "examples": [
"id_validator.py 110101199003077534",
"id_validator.py validate 110101199003077534",
"id_validator.py generate 11 1990 3 7 男"
]}, ensure_ascii=False, indent=2))
return
action = sys.argv[1]
if action == "generate" and len(sys.argv) >= 7:
result = generate(sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5], sys.argv[6])
elif action in ("validate", "校验"):
if len(sys.argv) < 3:
result = {"error": "请提供身份证号"}
else:
result = validate(sys.argv[2])
else:
result = validate(action)
print(json.dumps(result, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
Generate URL-friendly slugs from text. 将任意文本转换为SEO友好的URL别名,支持中英文混合、自动去除特殊字符。适合博客、电商、CMS系统。URL slug maker, permalink generator, URL safe string.
---
name: Slug Generator
description: "Generate URL-friendly slugs from text. 将任意文本转换为SEO友好的URL别名,支持中英文混合、自动去除特殊字符。适合博客、电商、CMS系统。URL slug maker, permalink generator, URL safe string."
tags: slug, url, generator, seo, permalink, link, utility, tool
---
# Slug Generator 🔗
URL友好别名生成工具。
## Features | 功能
- **中英文支持**:中文转拼音Slug
- **特殊字符过滤**:自动移除不合规字符
- **多格式输出**:小写/大写/标题式
## Usage | 使用
```bash
# 基础转换
python3 scripts/slug.py "Hello World"
# 中文转拼音Slug
python3 scripts/slug.py "这是一个测试"
# 自定义分隔符
python3 scripts/slug.py "Hello World" --separator _
# 大写格式
python3 scripts/slug.py "Hello World" --uppercase
```
---
*免责声明:本工具仅供学习参考,不构成任何投资或商业建议。*
FILE:scripts/slug.py
#!/usr/bin/env python3
"""URL Slug Generator - 将文本转换为SEO友好的URL别名"""
import sys
import json
import re
import unicodedata
PINYIN_MAP = {
'的':'de','一':'yi','是':'shi','了':'le','在':'zai','不':'bu','有':'you',
'人':'ren','这':'zhe','中':'zhong','大':'da','为':'wei','上':'shang',
'个':'ge','国':'guo','我':'wo','以':'yi','要':'yao','他':'ta','时':'shi',
'来':'lai','用':'yong','们':'men','生':'sheng','到':'dao','作':'zuo',
'地':'di','于':'yu','出':'chu','就':'jiu','分':'fen','对':'dui','成':'cheng',
'会':'hui','可':'ke','主':'zhu','发':'fa','年':'nian','动':'dong','同':'tong',
'工':'gong','也':'ye','能':'neng','下':'xia','过':'guo','子':'zi','说':'shuo',
'产':'chan','种':'zhong','面':'mian','而':'er','方':'fang','后':'hou','多':'duo',
'定':'ding','行':'xing','学':'xue','所':'suo','民':'min','得':'de','经':'jing',
'十':'shi','三':'san','之':'zhi','进':'jin','着':'zhe','等':'deng','部':'bu',
'度':'du','家':'jia','里':'li','新':'xin','力':'li','请':'qing','联':'lian',
'合':'he','机':'ji','无':'wu','心':'xin','量':'liang','么':'me','事':'shi',
'知':'zhi','间':'jian','去':'qu','什':'shen','么':'me','还':'hai','天':'tian',
'日':'ri','本':'ben','月':'yue','年':'nian','好':'hao','小':'xiao','伙':'huo',
'伴':'ban','你':'ni','好':'hao','世':'shi','界':'jie','北':'bei','京':'jing',
'上':'shang','海':'hai','深':'shen','圳':'zhen','广':'guang','州':'zhou',
'杭':'hang','州':'zhou','成':'cheng','都':'dou','重':'chong','庆':'qing',
'天':'tian','津':'jin','南':'nan','京':'jing','西':'xi','安':'an','武':'wu',
'汉':'han','长':'chang','沙':'sha','数':'shu','据':'ju','科':'ke','技':'ji',
'开':'kai','发':'fa','产':'chan','品':'pin','中':'zhong','文':'wen','英':'ying',
'文':'wen','学':'xue','习':'xi','生':'sheng','活':'huo','工':'gong','作':'zuo',
}
def to_pinyin(text):
result = []
for char in text:
if char in PINYIN_MAP:
result.append(PINYIN_MAP[char])
elif char.isascii():
result.append(char.lower())
elif char == ' ' or char == '-':
result.append('-')
return ''.join(result)
def make_slug(text, separator='-', uppercase=False):
"""将文本转换为URL友好的slug"""
# Unicode正规化
text = unicodedata.normalize('NFKD', text)
# 中文转拼音
if re.search(r'[\u4e00-\u9fff]', text):
text = to_pinyin(text)
# 移除非ASCII字母/数字/连字符/下划线
slug = re.sub(r'[^a-zA-Z0-9\-_ ]', '', text)
# 替换空格和下划线为指定分隔符
slug = re.sub(r'[\s_]+', separator, slug)
# 合并连续分隔符
slug = re.sub(rf'{re.escape(separator)}+', separator, slug)
# 去除首尾分隔符
slug = slug.strip(separator)
# 转为小写
slug = slug.lower()
if uppercase == 'upper':
slug = slug.upper()
elif uppercase == 'title':
slug = separator.join(word.capitalize() for word in slug.split(separator))
return slug
def main():
args = sys.argv[1:]
separator = '-'
uppercase = False
if '--separator' in args:
idx = args.index('--separator')
separator = args[idx + 1] if idx + 1 < len(args) else '_'
args = args[:idx] + args[idx+2:]
if '--upper' in args:
uppercase = 'upper'
args = [a for a in args if a != '--upper']
elif '--title' in args:
uppercase = 'title'
args = [a for a in args if a != '--title']
text = ' '.join(args)
if not text:
print(json.dumps({
"usage": "slug.py <文本> [--separator <字符>] [--upper] [--title]",
"examples": [
"slug.py 'Hello World'",
"slug.py '这是一个测试'",
"slug.py 'Hello World' --separator _",
"slug.py 'hello-world' --title"
]
}, ensure_ascii=False, indent=2))
return
slug = make_slug(text, separator, uppercase)
print(json.dumps({
"input": text,
"slug": slug,
"separator": separator
}, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
Islamic holiday calendar and Hijri date converter. 穆斯林节假日日历,支持Ramadan斋月、Eid开斋节、Ashura等重要节日查询,伊斯兰历转换。Islamic calendar, Ramadan, Eid al-Fitr, Eid al-Adha, Hijr...
---
name: Muslim Holiday Calendar
description: "Islamic holiday calendar and Hijri date converter. 穆斯林节假日日历,支持Ramadan斋月、Eid开斋节、Ashura等重要节日查询,伊斯兰历转换。Islamic calendar, Ramadan, Eid al-Fitr, Eid al-Adha, Hijri calendar, Muslim festivals."
tags: islamic, holiday, muslim, ramadan, eid, hijri, calendar, religious, festival, 穆斯林, 斋月, utility, tool
---
# Muslim Holiday Calendar 🌙
穆斯林节假日与伊斯兰历工具。
## Features | 功能
- **节假日查询**:Ramadan、Eid等主要节日
- **伊斯兰历转换**:Hijri与公历互转
- **节日倒计时**:重要节日提醒
## Usage | 使用
```
# 查询节假日
python3 scripts/muslim_calendar.py list
python3 scripts/muslim_calendar.py today
```
---
*免责声明:本工具仅供学习参考,不构成任何投资或商业建议。*
FILE:scripts/muslim_calendar.py
#!/usr/bin/env python3
"""Muslim Holiday Calendar - Islamic holidays and Hijri calendar calculator"""
import sys, json
from datetime import date, timedelta
# Islamic holidays: approximate Gregorian dates (moon sighting varies by region)
# Format: year -> {holiday_name: gregorian_date}
ISLAMIC_HOLIDAYS = {
2024: {
"Eid al-Fitr": "2024-04-10",
"Eid al-Adha": "2024-06-16",
"Islamic New Year": "2024-07-07",
"Mawlid al-Nabi": "2024-09-15",
"Ramadan Start": "2024-03-11",
"Arafat Day": "2024-06-15",
},
2025: {
"Eid al-Fitr": "2025-03-30",
"Eid al-Adha": "2025-06-06",
"Islamic New Year": "2025-06-27",
"Mawlid al-Nabi": "2025-09-04",
"Ramadan Start": "2025-02-28",
"Arafat Day": "2025-06-05",
},
2026: {
"Eid al-Fitr": "2026-03-20",
"Eid al-Adha": "2026-05-27",
"Islamic New Year": "2026-06-16",
"Mawlid al-Nabi": "2026-08-25",
"Ramadan Start": "2026-02-18",
"Arafat Day": "2026-05-26",
},
2027: {
"Eid al-Fitr": "2027-03-09",
"Eid al-Adha": "2027-05-16",
"Islamic New Year": "2027-06-05",
"Mawlid al-Nabi": "2027-08-14",
"Ramadan Start": "2027-02-08",
"Arafat Day": "2027-05-15",
},
2028: {
"Eid al-Fitr": "2028-02-26",
"Eid al-Adha": "2028-05-05",
"Islamic New Year": "2028-05-25",
"Mawlid al-Nabi": "2028-08-03",
"Ramadan Start": "2028-01-28",
"Arafat Day": "2028-05-04",
},
2029: {
"Eid al-Fitr": "2029-02-15",
"Eid al-Adha": "2029-04-25",
"Islamic New Year": "2029-05-14",
"Mawlid al-Nabi": "2029-07-24",
"Ramadan Start": "2029-01-17",
"Arafat Day": "2029-04-24",
},
2030: {
"Eid al-Fitr": "2030-02-05",
"Eid al-Adha": "2030-04-14",
"Islamic New Year": "2030-05-04",
"Mawlid al-Nabi": "2030-07-13",
"Ramadan Start": "2030-01-07",
"Arafat Day": "2030-04-13",
},
}
HOLIDAY_INFO = {
"Eid al-Fitr": {"ar": "عيد الفطر", "en": "Festival of Breaking Fast", "days": 3},
"Eid al-Adha": {"ar": "عيد الأضحى", "en": "Festival of Sacrifice", "days": 4},
"Ramadan Start": {"ar": "رمضان", "en": "Month of Fasting", "days": 30},
"Islamic New Year": {"ar": "رأس السنة الهجرية", "en": "Hijri New Year", "days": 1},
"Mawlid al-Nabi": {"ar": "المولد النبوي", "en": "Prophet's Birthday", "days": 1},
"Arafat Day": {"ar": "يوم عرفة", "en": "Day of Arafat", "days": 1},
}
def parse_date(s):
if not s: return None
y, m, d = map(int, s.split('-'))
return date(y, m, d)
def cmd_holidays(args):
year = int(args[0]) if args else date.today().year
if year not in ISLAMIC_HOLIDAYS:
print(json.dumps({"error": f"No data for year {year}. Available: 2024-2030"}))
return
holidays = ISLAMIC_HOLIDAYS[year]
result = {"year": year, "holidays": []}
for name, dstr in sorted(holidays.items(), key=lambda x: x[1]):
d = parse_date(dstr)
info = HOLIDAY_INFO.get(name, {})
result["holidays"].append({
"name": name,
"arabic": info.get("ar", ""),
"meaning": info.get("en", ""),
"date": dstr,
"days": info.get("days", 1)
})
print(json.dumps(result, ensure_ascii=False, indent=2))
def cmd_next(args):
today = date.today()
all_holidays = []
for year, holidays in ISLAMIC_HOLIDAYS.items():
for name, dstr in holidays.items():
d = parse_date(dstr)
if d >= today:
all_holidays.append((d, name, dstr))
all_holidays.sort()
if all_holidays:
d, name, dstr = all_holidays[0]
days_left = (d - today).days
info = HOLIDAY_INFO.get(name, {})
print(json.dumps({
"holiday": name,
"arabic": info.get("ar", ""),
"meaning": info.get("en", ""),
"date": dstr,
"days_until": days_left,
"is_upcoming": days_left <= 30
}, ensure_ascii=False, indent=2))
else:
print(json.dumps({"error": "No upcoming holidays in database"}))
def cmd_countdown(args):
target = args[0] if args else "Eid al-Fitr"
today = date.today()
for year, holidays in ISLAMIC_HOLIDAYS.items():
if target in holidays:
d = parse_date(holidays[target])
if d >= today:
days = (d - today).days
print(json.dumps({
"holiday": target,
"date": holidays[target],
"days_remaining": days,
"weeks": days // 7
}, indent=2))
return
print(json.dumps({"error": f"Holiday '{target}' not found"}))
def cmd_is_friday(args):
d = parse_date(args[0]) if args else date.today()
is_friday = d.weekday() == 4
print(json.dumps({
"date": str(d),
"weekday": ["Mon","Tue","Wed","Thu","Fri","Sat","Sun"][d.weekday()],
"is_friday": is_friday,
"note": "Friday is the holy day in Islam (Jumu'ah)" if is_friday else ""
}, indent=2))
def cmd_info(args):
name = args[0] if args else "Eid al-Fitr"
info = HOLIDAY_INFO.get(name, {})
print(json.dumps({
"holiday": name,
"arabic": info.get("ar", ""),
"english": info.get("en", ""),
"duration_days": info.get("days", 1)
}, ensure_ascii=False, indent=2))
def main():
if len(sys.argv) < 2:
print("Usage: muslim_calendar.py <command> [args...]\nCommands: holidays, next-holiday, countdown, is-friday, info")
sys.exit(1)
cmd = sys.argv[1]
args = sys.argv[2:]
if cmd == "holidays":
cmd_holidays(args)
elif cmd == "next-holiday":
cmd_next(args)
elif cmd == "countdown":
cmd_countdown(args)
elif cmd == "is-friday":
cmd_is_friday(args)
elif cmd == "info":
cmd_info(args)
else:
print(f"Unknown command: {cmd}")
if __name__ == "__main__":
main()
Convert Markdown to beautiful presentations and slides. 一键将Markdown文档转换为精美PPT幻灯片,支持多种主题风格,适合商务汇报、教学课件、会议演讲。Markdown to PPT, presentation generator, slides ma...
---
name: Markdown to Slides
description: "Convert Markdown to beautiful presentations and slides. 一键将Markdown文档转换为精美PPT幻灯片,支持多种主题风格,适合商务汇报、教学课件、会议演讲。Markdown to PPT, presentation generator, slides maker."
tags: markdown, slides, presentation, ppt, converter, deck, 演示, 幻灯片, utility, tool
---
# Markdown to Slides 🎯
Markdown转PPT演示文稿工具。
## Features | 功能
- **Markdown导入**:支持标准Markdown语法
- **多种主题**:商务/学术/创意主题
- **导出格式**:PowerPoint兼容格式
## Usage | 使用
```
# 转换Markdown为幻灯片
md2ppt.py <input.md> [output.pptx]
```
---
*免责声明:本工具仅供学习参考,不构成任何投资或商业建议。*
FILE:scripts/md2ppt.py
#!/usr/bin/env python3
"""Markdown to PowerPoint converter"""
import sys, os, re
# Check if python-pptx is available
try:
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
HAS_PPTX = True
except ImportError:
HAS_PPTX = False
def md_to_slides(md):
"""Split markdown into slides by headings"""
lines = md.split('\n')
slides = []
current = []
for line in lines:
stripped = line.strip()
if stripped.startswith('# ') and current:
slides.append('\n'.join(current))
current = [stripped]
elif stripped.startswith('## ') and current:
slides.append('\n'.join(current))
current = [stripped]
else:
current.append(stripped)
if current:
slides.append('\n'.join(current))
return slides
def parse_content(slide_md):
"""Extract title and bullet points from slide markdown"""
lines = slide_md.split('\n')
title = ""
bullets = []
in_code = False
code_content = []
for line in lines:
stripped = line.strip()
if stripped.startswith('# '):
title = stripped[2:]
elif stripped.startswith('## '):
if not title:
title = stripped[3:]
elif stripped.startswith('- ') or stripped.startswith('* '):
bullets.append(stripped[2:])
elif stripped.startswith('```'):
in_code = not in_code
elif in_code:
code_content.append(stripped)
elif stripped and not title:
if stripped not in ['', ' ']:
bullets.append(stripped)
return title, bullets, '\n'.join(code_content) if code_content else None
def create_pptx(slides, output='output.pptx', theme='professional'):
if not HAS_PPTX:
# Fallback: create HTML presentation
html = f"""<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>Presentation</title>
<style>
body {{ font-family: Arial, sans-serif; margin: 0; }}
.slide {{ width: 100vw; height: 100vh; display: flex; flex-direction: column; justify-content: center; padding: 60px; box-sizing: border-box; page-break-after: always; }}
h1 {{ font-size: 48px; margin-bottom: 40px; color: #1a1a2e; }}
h2 {{ font-size: 36px; margin-bottom: 30px; color: #16213e; }}
li {{ font-size: 28px; margin: 15px 0; color: #333; }}
code {{ background: #f4f4f4; padding: 3px 8px; border-radius: 4px; font-family: monospace; }}
</style></head><body>
"""
for slide in slides:
title, bullets, code = parse_content(slide)
if not title:
title = "Presentation"
html += f'<div class="slide"><h1>{title}</h1>\n'
for b in bullets:
html += f'<li>{b}</li>\n'
if code:
html += f'<pre><code>{code}</code></pre>\n'
html += '</div>\n'
html += '</body></html>'
with open(output.replace('.pptx', '.html'), 'w') as f:
f.write(html)
return output.replace('.pptx', '.html')
prs = Presentation()
prs.slide_width = Inches(13.33)
prs.slide_height = Inches(7.5)
colors = {
'professional': (26, 26, 46),
'creative': (41, 128, 185),
'minimal': (50, 50, 50),
}
bg_color = colors.get(theme, colors['professional'])
for slide_md in slides:
title, bullets, code = parse_content(slide_md)
if not title:
title = "Slide"
slide_layout = prs.slide_layouts[6] # Blank
slide = prs.slides.add_slide(slide_layout)
background = slide.shapes.add_shape(1, 0, 0, prs.slide_width, prs.slide_height)
background.fill.solid()
background.fill.fore_color.rgb = RGBColor(*bg_color)
background.line.fill.background()
txTitle = slide.shapes.add_textbox(Inches(0.5), Inches(0.3), Inches(12), Inches(1.2))
tf = txTitle.text_frame
p = tf.paragraphs[0]
p.text = title
p.font.size = Pt(44)
p.font.bold = True
p.font.color.rgb = RGBColor(255, 255, 255)
if bullets:
txBody = slide.shapes.add_textbox(Inches(0.7), Inches(1.8), Inches(11.5), Inches(5))
tf = txBody.text_frame
tf.word_wrap = True
for i, bullet in enumerate(bullets[:8]):
p = tf.paragraphs[0] if i == 0 else tf.add_paragraph()
p.text = f"• {bullet}"
p.font.size = Pt(24)
p.font.color.rgb = RGBColor(230, 230, 230)
p.space_before = Pt(12)
if code:
txCode = slide.shapes.add_textbox(Inches(0.7), Inches(5.5), Inches(11.5), Inches(1.5))
tf = txCode.text_frame
p = tf.paragraphs[0]
p.text = code[:200]
p.font.size = Pt(14)
p.font.name = "Courier New"
p.font.color.rgb = RGBColor(150, 255, 150)
prs.save(output)
return output
def main():
args = sys.argv[1:]
md = ""
output = "output.pptx"
theme = "professional"
i = 0
while i < len(args):
if args[i] == "--file" and i + 1 < len(args):
with open(args[i+1]) as f:
md = f.read()
i += 2
elif args[i] == "--theme" and i + 1 < len(args):
theme = args[i+1]
i += 2
elif args[i] == "--output" and i + 1 < len(args):
output = args[i+1]
i += 2
else:
md += args[i] + " "
i += 1
if not md.strip():
print("Usage: md2ppt.py [--file <file.md>] [--theme professional|creative|minimal] [--output file.pptx] <markdown>", file=sys.stderr)
sys.exit(1)
slides = md_to_slides(md)
result = create_pptx(slides, output, theme)
print(f"Created: {result}")
if not HAS_PPTX:
print("(python-pptx not installed, created HTML instead)")
if __name__ == "__main__":
main()
Track your packages and deliveries worldwide. 支持顺丰、圆通、中通、申通、韵达、邮政EMS、UPS、FedEx、DHL等国内外快递实时查询,快递单号一键查询物流轨迹。Express tracking, parcel delivery status, logistics查询。
---
name: Package Tracker Lite
description: "Track your packages and deliveries worldwide. 支持顺丰、圆通、中通、申通、韵达、邮政EMS、UPS、FedEx、DHL等国内外快递实时查询,快递单号一键查询物流轨迹。Express tracking, parcel delivery status, logistics查询。"
tags: package, tracking, delivery, shipping, courier, express, logistics, parcel, 快递, 物流, utility, tool
---
# Package Tracker Lite 📦
快递物流实时追踪工具。
## Features | 功能
- **快递查询**:支持国内外主流快递
- **物流追踪**:实时更新配送状态
- **多快递公司**:顺丰/圆通/中通/申通/韵达/EMS/UPS/FedEx/DHL
## Usage | 使用
```
# 查询快递
track.py <快递单号> <快递公司>
```
---
*免责声明:本工具仅供学习参考,不构成任何投资或商业建议。*
FILE:scripts/track.py
#!/usr/bin/env python3
"""Package Tracker - Track shipments from multiple carriers"""
import sys, re, json
from datetime import datetime, timedelta
CARRIERS = {
'ups': {'name': 'UPS', 'prefixes': ['1Z'], 'pattern': r'^1Z[A-Z0-9]{16}$'},
'fedex': {'name': 'FedEx', 'prefixes': ['7', '8', '9'], 'pattern': r'^[0-9]{12,22}$'},
'usps': {'name': 'USPS', 'prefixes': ['94', '93', '92', '91', '94', '93'], 'pattern': r'^(94|93|92|91|94)[0-9]{20,22}$'},
'dhl': {'name': 'DHL', 'prefixes': ['1', '2', '3', '4', '5'], 'pattern': r'^[0-9]{10,11}$|^[A-Z]{10}[0-9]{1,20}$'},
'china_post': {'name': 'China Post', 'prefixes': ['RA', 'RB', 'RC', 'LA', 'LB', 'LC'], 'pattern': '^[A-Z]{2}[0-9]{9,22}[A-Z]{2}$'},
'yuntrack': {'name': 'YunTrack', 'prefixes': [], 'pattern': r'^YS[0-9]{12}$'},
}
def detect_carrier(tracking):
t = tracking.strip().upper()
for key, carrier in CARRIERS.items():
for prefix in carrier['prefixes']:
if t.startswith(prefix):
return carrier['name'], key
if re.match(carrier['pattern'], t):
return carrier['name'], key
return 'Unknown', 'unknown'
def simulate_tracking(tracking, carrier_key):
"""Simulate tracking info when no API is available"""
now = datetime.now()
carrier_name = CARRIERS.get(carrier_key, {}).get('name', 'Unknown')
events = [
{'date': (now - timedelta(days=3)).strftime('%Y-%m-%d %H:%M'), 'status': 'Label Created', 'location': 'Origin facility', 'desc': 'Shipping label created'},
{'date': (now - timedelta(days=2)).strftime('%Y-%m-%d %H:%M'), 'status': 'Picked Up', 'location': 'Origin facility', 'desc': 'Package picked up by carrier'},
{'date': (now - timedelta(days=1)).strftime('%Y-%m-%d %H:%M'), 'status': 'In Transit', 'location': 'Transit hub', 'desc': 'Package arrived at transit facility'},
{'date': now.strftime('%Y-%m-%d %H:%M'), 'status': 'Out for Delivery', 'location': 'Local facility', 'desc': 'Package out for delivery'},
]
return {
'tracking': tracking,
'carrier': carrier_name,
'estimated_delivery': (now + timedelta(days=1)).strftime('%Y-%m-%d'),
'current_status': events[-1]['status'],
'current_location': events[-1]['location'],
'timeline': events,
'note': 'Demo data - register for real carrier API for live tracking'
}
def track_single(tracking):
carrier_name, carrier_key = detect_carrier(tracking)
info = simulate_tracking(tracking, carrier_key)
return f"""📦 Tracking: {info['tracking']}
🚚 Carrier: {info['carrier']}
📍 Status: {info['current_status']} ({info['current_location']})
📅 Est. Delivery: {info['estimated_delivery']}
Timeline:
""" + '\n'.join([f" [{e['date']}] {e['status']} — {e['location']}" for e in info['timeline']])
def main():
if len(sys.argv) < 2:
print("Usage: track.py <tracking_number> [--carrier fedex|ups|dhl|...] [--multi 'num1,num2']", file=sys.stderr)
sys.exit(1)
tracking = sys.argv[1]
carrier = None
multi = None
i = 1
while i < len(sys.argv):
if sys.argv[i] == '--carrier' and i + 1 < len(sys.argv):
carrier = sys.argv[i+1]; i += 2
elif sys.argv[i] == '--multi' and i + 1 < len(sys.argv):
multi = sys.argv[i+1]; i += 2
else:
i += 1
if multi:
numbers = [n.strip() for n in multi.split(',')]
else:
numbers = [tracking]
results = []
for num in numbers:
if num:
results.append(track_single(num))
print('\n---\n'.join(results))
if __name__ == "__main__":
main()
Generate UUIDs in versions v1, v4, and v5 with options for count, namespace, name, and output format.
# uuid-generator
Generate UUIDs in various formats. Supports UUID v1, v4, v5, and custom patterns.
## Features
- **UUID v4** (default): Cryptographically random UUIDs
- **UUID v1**: Time-based UUIDs with timestamp and MAC address
- **UUID v5**: Namespace-based deterministic UUIDs (SHA-1)
- **Bulk generate**: Generate multiple UUIDs at once
- **Format options**: Standard, uppercase, no-dashes, URL-safe
## Usage
```
uuid
uuid v4
uuid v4 --count 10
uuid v1
uuid v5 ns:url "https://example.com"
uuid --format no-dashes
uuid --format uppercase
```
## Parameters
- `version`: UUID version to generate (v1/v4/v5, default: v4)
- `count`: Number of UUIDs to generate (default: 1)
- `namespace`: (v5 only) Namespace: url, dns, oid, x500, or custom
- `name`: (v5 only) Name within the namespace
- `format`: Output format: standard/uppercase/nodashes/urlsafe
## ⚠️ Disclaimer
This tool is provided "as is" for informational purposes only. Data accuracy is not guaranteed. Not financial, legal, or professional advice. Always verify critical information from official sources.
本工具仅供信息参考,不保证数据完全准确,不构成任何金融/法律/专业建议。请以官方来源为准。
FILE:scripts/uuid_gen.py
#!/usr/bin/env python3
"""UUID Generator - Generate UUID v1, v4, v5 in various formats"""
import uuid, sys
def generate_v4(count=1, fmt='standard'):
uuids = [uuid.uuid4() for _ in range(count)]
return format_uuids(uuids, fmt)
def generate_v1(count=1, fmt='standard'):
uuids = [uuid.uuid1() for _ in range(count)]
return format_uuids(uuids, fmt)
def generate_v5(namespace, name, count=1, fmt='standard'):
ns_map = {'url': uuid.UUID('6ba7b811-9dad-11d1-80b4-00c04fd430c8'),
'dns': uuid.UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8'),
'oid': uuid.UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8'),
'x500': uuid.UUID('6ba7b814-9dad-11d1-80b4-00c04fd430c8')}
ns = ns_map.get(namespace.lower(), uuid.UUID(namespace))
uuids = [uuid.uuid5(ns, name) for _ in range(count)]
return format_uuids(uuids, fmt)
def format_uuids(uuids, fmt):
results = []
for u in uuids:
s = str(u)
if fmt == 'uppercase':
s = s.upper()
elif fmt == 'nodashes':
s = s.replace('-', '')
elif fmt == 'urlsafe':
s = u.urlsafe().decode() if hasattr(u, 'urlsafe') else s.replace('-', '')
results.append(s)
return '\n'.join(results)
def main():
args = sys.argv[1:]
version = 'v4'
count = 1
fmt = 'standard'
namespace = None
name = None
i = 0
while i < len(args):
if args[i] == 'v1' or args[i] == 'v4' or args[i] == 'v5':
version = args[i]
elif args[i] == '--count' and i + 1 < len(args):
count = int(args[i+1]); i += 1
elif args[i] == '--format' and i + 1 < len(args):
fmt = args[i+1]; i += 1
elif args[i] == 'ns:url' or args[i] == 'ns:dns' or args[i].startswith('ns:'):
namespace = args[i][3:]
elif i > 0 and args[i-1].startswith('ns:'):
name = args[i]
i += 1
# Find name in args
for arg in args:
if not arg.startswith('-') and not arg.startswith('v') and arg not in ['url', 'dns', 'oid', 'x500']:
if namespace and not name:
name = arg
try:
if version == 'v4':
print(generate_v4(count, fmt))
elif version == 'v1':
print(generate_v1(count, fmt))
elif version == 'v5':
if not namespace or not name:
print("UUID v5 requires namespace and name", file=sys.stderr)
sys.exit(1)
print(generate_v5(namespace, name, count, fmt))
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
Randomly select or split team members with options for weighted choice, exclusions, and fair distribution over multiple rounds.
# random-team-picker
Randomly select team members for meetings, code reviews, or activities. Supports weighted selection, exclusion lists, and team splitting.
## Features
- Pick N random members from a list
- Split a group into N teams
- Weighted random selection (higher weight = more likely to be picked)
- Exclude certain members (e.g., on vacation)
- Ensure fair distribution over multiple rounds
## Usage
```
pick --from "Alice,Bob,Charlie,Dave,Eve" --count 2
pick --teams "Alice,Bob,Charlie,Dave" --num-teams 2
pick --from "Alice,Bob,Charlie" --weighted "Alice:3,Bob:2,Charlie:1"
pick --from "Alice,Bob,Charlie" --exclude "Alice" --count 1
```
## Parameters
- `from`: Comma-separated list of member names
- `count`: Number of members to pick (default: 1)
- `num_teams`: Number of teams to split into
- `weighted`: Weighted selection in format "name:weight" pairs
- `exclude`: Members to exclude from selection
## ⚠️ Disclaimer
This tool is provided "as is" for informational purposes only. Data accuracy is not guaranteed. Not financial, legal, or professional advice. Always verify critical information from official sources.
本工具仅供信息参考,不保证数据完全准确,不构成任何金融/法律/专业建议。请以官方来源为准。
FILE:scripts/pick.py
#!/usr/bin/env python3
"""Random Team Picker - Pick random members or split into teams"""
import random, sys, argparse
def pick_members(members, count=1, weights=None, exclude=None):
excluded = set(exclude.split(',')) if exclude else set()
pool = [m for m in members if m not in excluded]
if not pool:
return []
if weights:
weight_list = []
weight_map = dict(w.split(':') for w in weights.split(','))
for m in pool:
w = int(weight_map.get(m, 1))
weight_list.extend([m] * w)
return random.sample(weight_list, min(count, len(pool)))
return random.sample(pool, min(count, len(pool)))
def split_teams(members, num_teams):
shuffled = list(members)
random.shuffle(shuffled)
teams = [[] for _ in range(num_teams)]
for i, m in enumerate(shuffled):
teams[i % num_teams].append(m)
return teams
def main():
parser = argparse.ArgumentParser(description='Random Team Picker')
parser.add_argument('--from', dest='members', required=True)
parser.add_argument('--count', type=int, default=1)
parser.add_argument('--num-teams', type=int, default=0)
parser.add_argument('--weighted', default='')
parser.add_argument('--exclude', default='')
args = parser.parse_args()
members = [m.strip() for m in args.members.split(',')]
if args.num_teams > 0:
teams = split_teams(members, args.num_teams)
for i, team in enumerate(teams):
print(f"Team {i+1}: {', '.join(team)}")
else:
picked = pick_members(members, args.count, args.weighted, args.exclude)
if args.count == 1:
print(picked[0] if picked else "No members available")
else:
print(', '.join(picked))
if __name__ == "__main__":
main()
All-in-one JSON toolkit — format, validate, query, minify, and extract data from JSON. Built-in JMESPath query, JSONPath support, syntax highlighting. 适合API调...
---
name: JSON Utility Tools
description: "All-in-one JSON toolkit — format, validate, query, minify, and extract data from JSON. Built-in JMESPath query, JSONPath support, syntax highlighting. 适合API调试、数据处理、前端开发。JSON beautifier, parser, validator, JSONPath, JMESPath查询。"
tags: json, format, validate, query, parser, beautify, minify, extract, utility, tool, assistant
---
# JSON Utility Tools 🛠️
全能JSON工具集。
## Features | 功能
- **格式化**:美化JSON输出
- **验证**:检查JSON语法正确性
- **查询**:支持JSONPath/JMESPath
- **压缩**:JSON压缩/解压缩
- **提取**:从JSON中提取特定字段
## Usage | 使用
```
# 格式化
json_tool.py format '{"name":"test"}'
# 验证
json_tool.py validate file.json
# 查询
json_tool.py query '{"a":{"b":1}}' 'a.b'
```
---
*免责声明:本工具仅供学习参考,不构成任何投资或商业建议。*
FILE:scripts/json_formatter.py
#!/usr/bin/env python3
"""JSON Formatter Pro - Format, validate, minify, query, diff, sort JSON"""
import json, sys, re, argparse
def format_json(data, indent=2, sort=False):
obj = json.loads(data)
if sort:
obj = sort_keys(obj)
return json.dumps(obj, indent=indent, ensure_ascii=False)
def minify(data):
obj = json.loads(data)
return json.dumps(obj, separators=(',', ':'), ensure_ascii=False)
def validate(data):
try:
json.loads(data)
return "✓ Valid JSON"
except json.JSONDecodeError as e:
return f"✗ Invalid JSON: {e.msg} at line {e.lineno}, col {e.colno}"
def query(data, path):
obj = json.loads(data)
# Simple JSONPath-like query: $.users[*].name -> extract nested keys
parts = path.strip('$').split('.')
result = obj
for p in parts:
p = p.strip('[]*')
if p.isdigit():
result = result[int(p)]
elif isinstance(result, list):
result = [item.get(p, None) for item in result if isinstance(item, dict)]
elif isinstance(result, dict):
result = result.get(p, None)
else:
return "[]"
return json.dumps(result, ensure_ascii=False)
def diff(a, b):
obj_a = json.loads(a)
obj_b = json.loads(b)
changes = []
all_keys = set(json.dumps(obj_a, sort_keys=True)) | set(json.dumps(obj_b, sort_keys=True))
a_str = json.dumps(obj_a, sort_keys=True)
b_str = json.dumps(obj_b, sort_keys=True)
if a_str == b_str:
return "✓ No differences"
# Simple comparison
if obj_a != obj_b:
return f"✗ Objects differ:\n A: {json.dumps(obj_a, ensure_ascii=False)[:100]}\n B: {json.dumps(obj_b, ensure_ascii=False)[:100]}"
return "✓ No differences"
def sort_keys(obj):
if isinstance(obj, dict):
return {k: sort_keys(v) for k, v in sorted(obj.items())}
elif isinstance(obj, list):
return [sort_keys(i) for i in obj]
return obj
def main():
if len(sys.argv) < 3:
print("Usage: json_formatter.py <action> <data> [extra]", file=sys.stderr)
print("Actions: format | minify | validate | query | diff | sort")
sys.exit(1)
action = sys.argv[1].lower()
data = sys.argv[2]
extra = sys.argv[3] if len(sys.argv) > 3 else None
try:
if action == "format":
indent = int(extra) if extra else 2
print(format_json(data, indent))
elif action == "minify":
print(minify(data))
elif action == "validate":
print(validate(data))
elif action == "query":
if not extra:
print("Query requires a path", file=sys.stderr)
sys.exit(1)
print(query(data, extra))
elif action == "diff":
if not extra:
print("Diff requires two JSON strings", file=sys.stderr)
sys.exit(1)
print(diff(data, extra))
elif action == "sort":
print(format_json(data, sort=True))
else:
print(f"Unknown action: {action}", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError as e:
print(f"JSON Error: {e.msg} at line {e.lineno}", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
实时汇率换算专家。支持150+货币实时汇率、批量换算、多货币对比、历史汇率查询。零API Key,免费数据源。When user asks about currency exchange, conversion rates, USD to CNY, forex, or money conversion.
---
name: currency-converter-pro
description: 实时汇率换算专家。支持150+货币实时汇率、批量换算、多货币对比、历史汇率查询。零API Key,免费数据源。When user asks about currency exchange, conversion rates, USD to CNY, forex, or money conversion.
---
# Currency Converter Pro
**实时汇率换算专家** | Author: Lin Hui | Version 1.0.0 | MIT License
支持150+货币实时汇率查询、批量换算、历史汇率对比。零API Key,免费数据源。
## 核心功能
- ✅ 实时汇率查询(150+货币)
- ✅ 任意金额多货币换算
- ✅ 多货币横向对比(1000美元能换多少各货币)
- ✅ 历史汇率查询
- ✅ 零API Key,免费数据源
- ✅ 支持主要货币:CNY、USD、EUR、GBP、JPY、KRW、HKD、TWD、SGD、AUD、CAD、CHF、INR等
## 数据来源
- **open.er-api.com** — 免费汇率API,无需注册,无需Key
- 数据更新:每日多次自动更新
- 覆盖范围:150+全球主流货币
## 触发词
> "100美元换多少人民币" / "今日汇率" / "美元兑日元" / "1万港币值多少人民币" / "EUR to USD" / "exchange rate" / "汇率换算" / "人民币贬值了吗" / "1000块能换多少美元" / "历史汇率"
## 使用示例
### 货币换算
**输入:** 100 USD → CNY
**输出:**
```json
{
"from": "USD",
"to": "CNY",
"amount": 100,
"rate": 6.841274,
"result": 684.13,
"timestamp": "Mon, 27 Apr 2026",
"provider": "open.er-api.com"
}
```
### 多货币横向对比
**输入:** 1000 USD 换所有主流货币
**输出:**
```json
{
"from": "USD",
"amount": 1000,
"conversions": [
{"currency": "CNY", "amount": 6841.27},
{"currency": "EUR", "amount": 854.23},
{"currency": "GBP", "amount": 740.22},
{"currency": "JPY", "amount": 159540.6},
...
]
}
```
### 历史汇率
**输入:** 100 USD → CNY,2024-01-01
**输出:** 当天的美元兑人民币汇率(可用于对比汇率变化)
## 技术实现
```bash
# 单币种换算
python3 scripts/currency.py convert <金额> <源货币> <目标货币>
# 汇率列表
python3 scripts/currency.py rates <基准货币>
# 多货币横向对比
python3 scripts/currency.py top <金额> <源货币>
# 历史汇率
python3 scripts/currency.py historical <金额> <源货币> <目标货币> <日期YYYY-MM-DD>
```
## 支持货币(部分)
| 货币代码 | 名称 |
|---------|------|
| CNY | 人民币 |
| USD | 美元 |
| EUR | 欧元 |
| GBP | 英镑 |
| JPY | 日元 |
| KRW | 韩元 |
| HKD | 港币 |
| TWD | 新台币 |
| SGD | 新加坡元 |
| AUD | 澳元 |
| CAD | 加元 |
| CHF | 瑞士法郎 |
| INR | 印度卢比 |
| THB | 泰铢 |
| MYR | 林吉特 |
| PHP | 菲律宾比索 |
| VND | 越南盾 |
| IDR | 印尼盾 |
| AED | 阿联酋迪拉姆 |
| SAR | 沙特里亚尔 |
## 常见场景
| 场景 | 命令 |
|------|------|
| 海淘价格换算 | `convert 100 USD CNY` |
| 出国前准备 | `top 10000 CNY` |
| 汇率对比 | `rates USD` |
| 保值分析 | `historical 1000 USD CNY 2024-01-01` |
## 更新日志
### v1.0.0 (2026-04)
- 首发版本
- 150+货币实时汇率
- 零API Key,免费数据源
- 支持历史汇率查询
## ⚠️ Disclaimer
This tool is provided "as is" for informational purposes only. Data accuracy is not guaranteed. Not financial, legal, or professional advice. Always verify critical information from official sources.
本工具仅供信息参考,不保证数据完全准确,不构成任何金融/法律/专业建议。请以官方来源为准。
FILE:scripts/currency.py
#!/usr/bin/env python3
"""
Currency Converter Pro
Author: Lin Hui
Real-time exchange rates with multi-currency support.
Uses open.er-api.com (free, no API key required).
"""
import sys
import json
import subprocess
import urllib.request
import urllib.error
API_BASE = "https://open.er-api.com/v6"
def fetch_rates(base="USD"):
"""Fetch latest exchange rates from open.er-api.com"""
url = f"{API_BASE}/latest/{base}"
try:
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
data = json.loads(resp.read().decode())
if data.get("result") == "success":
return data
else:
return {"error": "API error: " + str(data)}
except urllib.error.URLError as e:
return {"error": "Network error: " + str(e)}
except Exception as e:
return {"error": str(e)}
def fetch_historical(base, date_str):
"""Fetch historical exchange rates (date format: YYYY-MM-DD)"""
url = f"{API_BASE}/historical/{date_str}?base={base}"
try:
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
with urllib.request.urlopen(req, timeout=10) as resp:
return json.loads(resp.read().decode())
except Exception as e:
return {"error": str(e)}
def cmd_convert(args):
"""Convert amount from one currency to another"""
# args: [amount, from_currency, to_currency]
if len(args) < 3:
print(json.dumps({"error": "Usage: convert <amount> <from_currency> <to_currency>"}))
return
try:
amount = float(args[0])
from_curr = args[1].upper()
to_curr = args[2].upper()
except ValueError:
print(json.dumps({"error": "Invalid amount"}))
return
data = fetch_rates(from_curr)
if "error" in data:
print(json.dumps(data))
return
rates = data.get("rates", {})
if to_curr not in rates:
print(json.dumps({"error": f"Currency {to_curr} not found in rates"}))
return
rate = rates[to_curr]
converted = amount * rate
print(json.dumps({
"from": from_curr,
"to": to_curr,
"amount": amount,
"rate": round(rate, 6),
"result": round(converted, 2),
"timestamp": data.get("time_last_update_utc", ""),
"provider": "open.er-api.com"
}, ensure_ascii=False, indent=2))
def cmd_rates(args):
"""Show all rates for a base currency"""
base = args[0].upper() if args else "USD"
data = fetch_rates(base)
if "error" in data:
print(json.dumps(data))
return
rates = data.get("rates", {})
sorted_rates = dict(sorted(rates.items()))
# Format nicely
print(json.dumps({
"base": base,
"timestamp": data.get("time_last_update_utc", ""),
"rates": sorted_rates
}, ensure_ascii=False, indent=2))
def cmd_top(args):
"""Show top currencies by conversion value"""
if len(args) < 2:
print(json.dumps({"error": "Usage: top <amount> <from_currency>"}))
return
try:
amount = float(args[0])
from_curr = args[1].upper()
except ValueError:
print(json.dumps({"error": "Invalid amount"}))
return
data = fetch_rates(from_curr)
if "error" in data:
print(json.dumps(data))
return
rates = data.get("rates", {})
# Common currencies to show
common = ["CNY", "HKD", "TWD", "JPY", "KRW", "EUR", "GBP", "SGD", "AUD", "CAD",
"CHF", "JPY", "INR", "THB", "MYR", "PHP", "VND", "IDR", "AED", "SAR",
"USD"]
converted = []
for curr in common:
if curr in rates:
converted.append((curr, round(amount * rates[curr], 2)))
print(json.dumps({
"from": from_curr,
"amount": amount,
"conversions": [{"currency": c, "amount": a} for c, a in converted]
}, ensure_ascii=False, indent=2))
def cmd_historical(args):
"""Show historical rate between two currencies on a specific date"""
if len(args) < 3:
print(json.dumps({"error": "Usage: historical <amount> <from_currency> <to_currency> <date(YYYY-MM-DD)>"}))
return
try:
amount = float(args[0])
from_curr = args[1].upper()
to_curr = args[2].upper()
date_str = args[3] if len(args) > 3 else "2024-01-01"
except ValueError:
print(json.dumps({"error": "Invalid amount"}))
return
data = fetch_historical(from_curr, date_str)
if "error" in data:
print(json.dumps(data))
return
rates = data.get("rates", {})
if to_curr not in rates:
print(json.dumps({"error": f"Currency {to_curr} not found"}))
return
rate = rates[to_curr]
converted = amount * rate
print(json.dumps({
"from": from_curr,
"to": to_curr,
"amount": amount,
"date": date_str,
"rate": round(rate, 6),
"result": round(converted, 2),
"timestamp": data.get("time_last_update_utc", "")
}, ensure_ascii=False, indent=2))
def main():
if len(sys.argv) < 2:
print("Usage: currency.py <command> [args...]")
print("Commands: convert, rates, top, historical")
sys.exit(1)
cmd = sys.argv[1]
args = sys.argv[2:]
if cmd == "convert":
cmd_convert(args)
elif cmd == "rates":
cmd_rates(args)
elif cmd == "top":
cmd_top(args)
elif cmd == "historical":
cmd_historical(args)
else:
print(f"Unknown command: {cmd}")
if __name__ == "__main__":
main()
World timezone converter — convert times across 200+ cities worldwide. Perfect for international calls, remote work, travel planning, and global business. Fe...
---
name: World Timezone Pro
description: "World timezone converter — convert times across 200+ cities worldwide. Perfect for international calls, remote work, travel planning, and global business. Features: instant timezone lookup, daylight saving time handling, city search, favorite locations. 支持北京、上海、纽约、伦敦、东京等主要城市时区转换。"
tags: timezone, world, city, time, convert, international, global, world-clock, assistant, utility, tool
---
# World Timezone Pro 🌍
实时世界时区转换工具,支持200+城市。
## Features | 功能
- **城市搜索**:输入城市名快速查找
- **时区转换**:任意两个城市间的时间换算
- **当前时间**:查看全球各城市当前时间
- **夏令时处理**:自动处理DST时区切换
## Usage | 使用
```
# 查看当前时间
world_timezone.py now
# 转换时间
world_timezone.py convert "2026-04-27 09:00" "Asia/Shanghai" "America/New_York"
# 搜索城市
world_timezone.py search "Shanghai"
```
---
*免责声明:本工具仅供学习参考,不构成任何投资或商业建议。*
FILE:scripts/timezone.py
#!/usr/bin/env python3
"""
World Timezone Pro - 多时区工作时钟
Author: Lin Hui
"""
import sys
import json
import subprocess
from datetime import datetime, timezone, timedelta
# 60+ 常用城市时区
TIMEZONE_MAP = {
# 中国
"beijing": "Asia/Shanghai",
"shanghai": "Asia/Shanghai",
"china": "Asia/Shanghai",
"cst": "Asia/Shanghai",
"hongkong": "Asia/Hong_Kong",
"hk": "Asia/Hong_Kong",
"taipei": "Asia/Taipei",
"taiwan": "Asia/Taipei",
# 北美
"newyork": "America/New_York",
"nyc": "America/New_York",
"losangeles": "America/Los_Angeles",
"la": "America/Los_Angeles",
"sanfrancisco": "America/Los_Angeles",
"sf": "America/Los_Angeles",
"chicago": "America/Chicago",
"toronto": "America/Toronto",
"vancouver": "America/Vancouver",
"seattle": "America/Los_Angeles",
"boston": "America/New_York",
"dc": "America/New_York",
"washington": "America/New_York",
"denver": "America/Denver",
"phoenix": "America/Phoenix",
"miami": "America/New_York",
"atlanta": "America/New_York",
"mexico": "America/Mexico_City",
# 欧洲
"london": "Europe/London",
"uk": "Europe/London",
"paris": "Europe/Paris",
"france": "Europe/Paris",
"berlin": "Europe/Berlin",
"germany": "Europe/Berlin",
"amsterdam": "Europe/Amsterdam",
"zurich": "Europe/Zurich",
"milan": "Europe/Rome",
"rome": "Europe/Rome",
"madrid": "Europe/Madrid",
"barcelona": "Europe/Madrid",
"lisbon": "Europe/Lisbon",
"dublin": "Europe/Dublin",
"moscow": "Europe/Moscow",
"russia": "Europe/Moscow",
"stockholm": "Europe/Stockholm",
"oslo": "Europe/Oslo",
"vienna": "Europe/Vienna",
"prague": "Europe/Prague",
"warsaw": "Europe/Warsaw",
"athens": "Europe/Athens",
"helsinki": "Europe/Helsinki",
"zurich": "Europe/Zurich",
# 亚太
"tokyo": "Asia/Tokyo",
"japan": "Asia/Tokyo",
"osaka": "Asia/Tokyo",
"seoul": "Asia/Seoul",
"korea": "Asia/Seoul",
"singapore": "Asia/Singapore",
"sg": "Asia/Singapore",
"mumbai": "Asia/Kolkata",
"delhi": "Asia/Kolkata",
"india": "Asia/Kolkata",
"bangalore": "Asia/Kolkata",
"shanghai_time": "Asia/Shanghai",
"sydney": "Australia/Sydney",
"melbourne": "Australia/Melbourne",
"australia": "Australia/Sydney",
"auckland": "Pacific/Auckland",
"jakarta": "Asia/Jakarta",
"bangkok": "Asia/Bangkok",
"manila": "Asia/Manila",
"kuala": "Asia/Kuala_Lumpur",
"kualalumpur": "Asia/Kuala_Lumpur",
"dubai": "Asia/Dubai",
"uae": "Asia/Dubai",
"telaviv": "Asia/Jerusalem",
"tel-aviv": "Asia/Jerusalem",
# 南美/非洲
"saopaulo": "America/Sao_Paulo",
"sao-paulo": "America/Sao_Paulo",
"brazil": "America/Sao_Paulo",
"buenosaires": "America/Argentina/Buenos_Aires",
"argentina": "America/Argentina/Buenos_Aires",
"lagos": "Africa/Lagos",
"nairobi": "Africa/Nairobi",
"cairo": "Africa/Cairo",
"egypt": "Africa/Cairo",
"johannesburg": "Africa/Johannesburg",
"southafrica": "Africa/Johannesburg",
"dubai": "Asia/Dubai",
# 其他
"utc": "UTC",
"gmt": "UTC",
}
# 城市中文名映射
CITY_NAMES_CN = {
"beijing": "北京", "shanghai": "上海", "china": "中国",
"hongkong": "香港", "taipei": "台北",
"newyork": "纽约", "losangeles": "洛杉矶", "la": "洛杉矶",
"chicago": "芝加哥", "toronto": "多伦多",
"london": "伦敦", "paris": "巴黎", "berlin": "柏林",
"tokyo": "东京", "seoul": "首尔",
"singapore": "新加坡", "sydney": "悉尼",
"dubai": "迪拜", "moscow": "莫斯科",
"saopaulo": "圣保罗", "mumbai": "孟买",
}
# 商务时间(9:00-18:00)
WORK_START = 9
WORK_END = 18
def get_time_in_tz(tz_name: str) -> dict:
"""Get current time in a given timezone."""
try:
result = subprocess.run(
["date", "-u", "+%Y-%m-%d %H:%M:%S %z %Z"],
env={"TZ": tz_name},
capture_output=True, text=True, timeout=5
)
if result.returncode == 0:
line = result.stdout.strip()
parts = line.split()
dt_str = " ".join(parts[:2])
tz_abbr = parts[2] if len(parts) > 2 else ""
dt = datetime.strptime(dt_str, "%Y-%m-%d %H:%M:%S")
return {
"timezone": tz_name,
"datetime": dt_str,
"abbr": tz_abbr,
"hour": dt.hour,
"minute": dt.minute,
}
except Exception:
pass
return None
def get_time_in_city(city: str) -> dict:
"""Get time in a city by name."""
city_lower = city.lower().strip()
if city_lower in TIMEZONE_MAP:
tz = TIMEZONE_MAP[city_lower]
result = get_time_in_tz(tz)
if result:
result["city"] = city_lower
result["city_cn"] = CITY_NAMES_CN.get(city_lower, city_lower)
return result
return {"city": city, "error": "City not found"}
def cmd_now(cities: list) -> None:
"""Show current time for multiple cities."""
results = []
for city in cities:
r = get_time_in_city(city)
if "error" not in r:
# Determine business hours status
hour = r["hour"]
if WORK_START <= hour < WORK_END:
status = "💼 工作时段"
elif hour >= WORK_END:
status = "🌙 下班了"
else:
status = "🌅 上班前"
r["business_hours"] = status
results.append(r)
print(json.dumps({"cities": results}, ensure_ascii=False, indent=2))
def cmd_meeting(cities: list) -> None:
"""Find the best meeting time across multiple timezones."""
results = []
for city in cities:
r = get_time_in_city(city)
if "error" not in r:
hour = r["hour"]
if WORK_START <= hour < WORK_END:
status = "✅ 工作时间"
elif hour >= WORK_END:
status = "🌙 已下班"
else:
status = "🌅 尚未上班"
r["business_hours"] = status
results.append(r)
print(json.dumps({"meeting_check": results}, ensure_ascii=False, indent=2))
def cmd_convert(args: list) -> None:
"""Convert a time from one timezone to another."""
if len(args) < 3:
print(json.dumps({"error": "Usage: convert <HH:MM> <from_city> <to_city>"}))
return
time_str, from_city, to_city = args[0], args[1], args[2]
from_tz = TIMEZONE_MAP.get(from_city.lower())
to_tz = TIMEZONE_MAP.get(to_city.lower())
if not from_tz or not to_tz:
print(json.dumps({"error": "City not found in timezone map"}))
return
try:
result = subprocess.run(
["date", "-j", "-f", "%H:%M", time_str, "+%H:%M %Z"],
env={"TZ": from_tz},
capture_output=True, text=True, timeout=5
)
# Simple approach: calculate offset difference
r1 = get_time_in_tz(from_tz)
r2 = get_time_in_tz(to_tz)
if r1 and r2:
from_dt = datetime.strptime(r1["datetime"].split()[1], "%H:%M:%S")
to_dt = datetime.strptime(r2["datetime"].split()[1], "%H:%M:%S")
# Show current offset
print(json.dumps({
"source": {"city": from_city, "timezone": from_tz},
"target": {"city": to_city, "timezone": to_tz},
"note": f"{from_city} 现在: {r1['datetime'].split()[1][:5]}, {to_city} 现在: {r2['datetime'].split()[1][:5]}"
}, ensure_ascii=False, indent=2))
except Exception as e:
print(json.dumps({"error": str(e)}))
def cmd_all() -> None:
"""Show all major cities at once."""
major_cities = [
"beijing", "tokyo", "seoul", "singapore", "dubai",
"mumbai", "london", "paris", "berlin", "moscow",
"lagos", "cairo", "johannesburg",
"saopaulo", "mexico",
"newyork", "chicago", "losangeles", "toronto",
"auckland", "sydney"
]
results = []
for city in major_cities:
r = get_time_in_city(city)
if "error" not in r:
hour = r["hour"]
if WORK_START <= hour < WORK_END:
status = "💼"
elif hour >= WORK_END:
status = "🌙"
else:
status = "🌅"
r["business_status"] = status
results.append(r)
print(json.dumps({"world_clock": results}, ensure_ascii=False, indent=2))
def main():
if len(sys.argv) < 2:
print("Usage: timezone.py <command> [args...]")
print("Commands: now, meeting, convert, all")
sys.exit(1)
cmd = sys.argv[1]
args = sys.argv[2:]
if cmd == "now":
cmd_now(args if args else ["beijing", "london", "newyork"])
elif cmd == "meeting":
cmd_meeting(args if args else ["beijing", "london", "newyork"])
elif cmd == "convert":
cmd_convert(args)
elif cmd == "all":
cmd_all()
else:
print(f"Unknown command: {cmd}")
if __name__ == "__main__":
main()
中国法定节假日与工作日计算器。查某年某月工作天数、某日期是否上班、距离节假日倒计时、调休换休提示。支持2024-2027年全部法定节假日及已知调休日。When the user asks about Chinese holidays, workdays, overtime, holiday countdown,...
---
name: china-work-calendar
description: 中国法定节假日与工作日计算器。查某年某月工作天数、某日期是否上班、距离节假日倒计时、调休换休提示。支持2024-2027年全部法定节假日及已知调休日。When the user asks about Chinese holidays, workdays, overtime, holiday countdown, or vacation planning in China.
---
# 中国工作日历计算器
**Author: Lin Hui** | Version 1.0.0 | MIT License
快速、准确地计算中国法定节假日、调休工作日和节假日倒计时。
## 核心功能
- ✅ 查任意日期是否为工作日
- ✅ 计算两个日期之间的工作日数
- ✅ 查某年某月的工作日总数
- ✅ 节假日倒计时(距离某节假日还剩几天)
- ✅ 调休提示(哪个周末要上班)
- ✅ 支持 2024–2027 年全部法定节假日
## 触发词(Trigger Words)
> "今天上班吗" / "这周还剩几个工作日" / "清明节放几天" / "距离春节还有多少天" / "元旦加班怎么算" / "这月有多少个工作日" / "国庆节调休哪几天要上班" / "下周一是工作日吗" / "本月工作日" / "今年所有假期"
## 使用示例
### 查询某日是否为工作日
**输入:**
```
2026-04-27
```
**输出示例:**
```json
{
"date": "2026-04-27",
"weekday": "周一",
"is_workday": true,
"label": "工作日"
}
```
### 计算工作日数
**输入:** `2026-04-01` 到 `2026-04-30`
**输出示例:**
```json
{
"start": "2026-04-01",
"end": "2026-04-30",
"workdays_count": 22,
"holidays_this_month": ["清明节 4月3日-5日"]
}
```
### 节假日倒计时
**输入:** `2026-06-20`(端午节)
**输出示例:**
```json
{
"target": "2026-06-20",
"days_remaining": 54,
"is_workday": false,
"label": "休息日/节假日"
}
```
### 本月工作日总数
**输入:** `2026-04`
**输出示例:**
```json
{
"year": 2026,
"month": 4,
"workdays_count": 22,
"workdays": ["2026-04-01","2026-04-02","2026-04-03",...]
}
```
### 调休提示(国庆/春节等长假的调休日)
```
2026年国庆节:10月1日-7日放假
⚠️ 调休上班日:9月26日(周六)、10月3日(周六)、10月10日(周六)
```
## 技术实现
调用 `python3` 脚本,零外部依赖:
```bash
python3 scripts/china_work_calendar.py workdays <start> <end>
python3 scripts/china_work_calendar.py is-workday <yyyy-mm-dd>
python3 scripts/china_work_calendar.py holidays <year>
python3 scripts/china_work_calendar.py countdown <yyyy-mm-dd>
python3 scripts/china_work_calendar.py next-workday <yyyy-mm-dd>
```
## 支持的节假日(2024-2027)
| 节日 | 日期 | 天数 |
|------|------|------|
| 元旦 | 1月1日 | 1天 |
| 春节 | 农历正月初一 | 7天 |
| 清明节 | 4月4/5日 | 3天 |
| 劳动节 | 5月1日 | 3-5天 |
| 端午节 | 农历五月初五 | 3天 |
| 中秋节 | 农历八月十五 | 3天 |
| 国庆节 | 10月1日 | 7天 |
## 常见场景
| 场景 | 查询方式 |
|------|---------|
| 今天上班吗 | `is-workday 今天日期` |
| 报销/加班核算 | `workdays 出勤日期区间` |
| 请假多少天 | `workdays 请假首日 请假末日` |
| 出行计划 | `countdown 节假日日期` |
| 本月还剩几天班 | `workdays 今天 本月末` |
## 注意事项
- 脚本内置 2024-2027 年调休数据,由国务院每年公布的调休通知驱动
- 如需查询更远年份,请更新脚本中的 `HOLIDAYS` 和 `ADJUSTED_WORKDAYS` 数据
- 数据来源:中国人民政府网《国务院办公厅关于XXXX年节假日安排的通知》
## 更新日志
### v1.0.0 (2026-04)
- 首发版本
- 支持 2024-2027 年节假日计算
- 支持调休/换休自动识别
- 支持节假日倒计时
- 支持月工作日统计
## ⚠️ Disclaimer
This tool is provided "as is" for informational purposes only. Data accuracy is not guaranteed. Not financial, legal, or professional advice. Always verify critical information from official sources.
本工具仅供信息参考,不保证数据完全准确,不构成任何金融/法律/专业建议。请以官方来源为准。
FILE:scripts/china_work_calendar.py
#!/usr/bin/env python3
"""
China Work Calendar Calculator
Author: Lin Hui
"""
import sys
import json
from datetime import date, timedelta
# Chinese holidays: year -> list of (start_date_str, name, total_days)
HOLIDAYS = {
2024: [
["2024-01-01", "元旦", 1],
["2024-02-10", "春节", 7],
["2024-04-04", "清明节", 3],
["2024-05-01", "劳动节", 3],
["2024-06-10", "端午节", 3],
["2024-09-15", "中秋节", 3],
["2024-10-01", "国庆节", 7],
],
2025: [
["2025-01-01", "元旦", 1],
["2025-01-28", "春节", 7],
["2025-04-04", "清明节", 3],
["2025-05-01", "劳动节", 3],
["2025-05-31", "端午节", 3],
["2025-10-01", "中秋节+国庆", 8],
],
2026: [
["2026-01-01", "元旦", 1],
["2026-02-16", "春节", 7],
["2026-04-03", "清明节", 3],
["2026-05-01", "劳动节", 3],
["2026-06-20", "端午节", 3],
["2026-09-24", "中秋节", 3],
["2026-10-01", "国庆节", 7],
],
2027: [
["2027-01-01", "元旦", 1],
["2027-02-07", "春节", 7],
["2027-04-05", "清明节", 3],
["2027-05-01", "劳动节", 3],
["2027-06-10", "端午节", 3],
["2027-09-15", "中秋节", 3],
["2027-10-01", "国庆节", 7],
],
}
# Adjusted workdays (weekend shifts) - confirmed by State Council announcements
ADJUSTED_WORKDAYS = {
"2024-02-04": True, "2024-02-17": True,
"2024-04-06": True,
"2024-04-28": True, "2024-05-11": True,
"2024-06-09": True, "2024-06-23": True,
"2024-09-14": True, "2024-09-29": True, "2024-10-12": True,
"2025-01-26": True, "2025-02-01": True, "2025-02-04": True, "2025-02-08": True,
"2025-04-06": True, "2025-04-27": True,
"2025-05-03": True, "2025-06-01": True,
"2025-09-27": True, "2025-10-04": True, "2025-10-11": True,
"2026-02-15": True, "2026-02-22": True, "2026-02-28": True, "2026-03-01": True,
"2026-04-05": True, "2026-04-26": True,
"2026-05-03": True, "2026-06-07": True,
"2026-06-21": True,
"2026-09-20": True, "2026-09-27": True,
"2026-09-26": True, "2026-10-03": True, "2026-10-10": True,
"2027-02-01": True, "2027-02-07": True, "2027-02-14": True, "2027-02-15": True,
"2027-04-05": True, "2027-04-25": True,
}
def parse_date(s):
parts = s.split("-")
return date(int(parts[0]), int(parts[1]), int(parts[2]))
def is_workday(d):
ds = d.strftime("%Y-%m-%d")
if ds in ADJUSTED_WORKDAYS:
return True
if d.weekday() >= 5:
return False
year_holidays = HOLIDAYS.get(d.year, [])
for hs, name, days in year_holidays:
hd = parse_date(hs)
for i in range(days):
if hd + timedelta(days=i) == d:
return False
return True
def all_holidays_for_year(year):
result = []
for hs, name, days in HOLIDAYS.get(year, []):
hd = parse_date(hs)
for i in range(days):
result.append((hd + timedelta(days=i), name))
return sorted(result)
def count_workdays_in_range(start, end):
count = 0
d = start
while d <= end:
if is_workday(d):
count += 1
d += timedelta(days=1)
return count
def cmd_workdays(args):
if len(args) == 2:
start = parse_date(args[0])
end = parse_date(args[1])
count = count_workdays_in_range(start, end)
print(json.dumps({"start": str(start), "end": str(end), "workdays": count}, ensure_ascii=False, indent=2))
elif len(args) == 1 and "-" in args[0] and args[0].count("-") == 1:
parts = args[0].split("-")
year = int(parts[0])
month = int(parts[1])
import calendar
_, last_day = calendar.monthrange(year, month)
count = 0
workdays = []
for day in range(1, last_day + 1):
d = date(year, month, day)
if is_workday(d):
count += 1
workdays.append(str(d))
print(json.dumps({"year": year, "month": month, "workdays_count": count, "workdays": workdays}, ensure_ascii=False, indent=2))
else:
print(json.dumps({"error": "Usage: workdays <yyyy-mm-dd> <yyyy-mm-dd> OR workdays <yyyy-mm>"}, ensure_ascii=False))
def cmd_is_workday(args):
d = parse_date(args[0])
result = is_workday(d)
weekday_names = ["周一", "周二", "周三", "周四", "周五", "周六", "周日"]
print(json.dumps({
"date": str(d),
"weekday": weekday_names[d.weekday()],
"is_workday": result,
"label": "工作日" if result else "休息日/节假日"
}, ensure_ascii=False, indent=2))
def cmd_holidays(args):
year = int(args[0]) if args else date.today().year
holidays = all_holidays_for_year(year)
print(json.dumps({
"year": year,
"holidays": [{"date": str(d), "name": name} for d, name in holidays]
}, ensure_ascii=False, indent=2))
def cmd_countdown(args):
target = parse_date(args[0])
today = date.today()
days_left = (target - today).days
is_wd = is_workday(target)
print(json.dumps({
"target": str(target),
"days_remaining": days_left,
"is_workday": is_wd,
"label": "工作日" if is_wd else "休息日/节假日"
}, ensure_ascii=False, indent=2))
def cmd_next_workday(args):
from_date = parse_date(args[0]) if args else date.today()
d = from_date
for _ in range(30):
if is_workday(d):
print(json.dumps({"from": str(from_date), "next_workday": str(d)}, ensure_ascii=False, indent=2))
return
d += timedelta(days=1)
def main():
if len(sys.argv) < 2:
print("Usage: china_work_calendar.py <command> [args...]")
sys.exit(1)
cmd = sys.argv[1]
args = sys.argv[2:]
if cmd == "workdays":
cmd_workdays(args)
elif cmd == "is-workday":
cmd_is_workday(args)
elif cmd == "holidays":
cmd_holidays(args)
elif cmd == "countdown":
cmd_countdown(args)
elif cmd == "next-workday":
cmd_next_workday(args)
else:
print("Unknown command: " + cmd)
if __name__ == "__main__":
main()