0. 前言
这个是我最近决定开设的栏目,用于记录一些突发奇想,或者闲来无事,用Python开发制作的小工具小游戏,希望能在练习中提升一下自己!
2025/09/17 更新:
keyboard_arrow_down1. 修复了打印闪屏的问题 2. 增加日薪计算功能 3. 新增了彩色打印
1. 概念
拿着月薪都在想日薪多少多少,但那些大佬就不一样了,用秒来计算每秒挣了多少钱!
那我们能不能在Python中实现一下?
2. 开发
先贴代码:
Python
############ 配置区 ############
import chinese_calendar as holiday # 判断是否为中国节假日 盈透版本2025
from datetime import datetime, timedelta
import time
import os
import base64
import ctypes
entry = "2025/07/20" # 入职日期
mySalary = b'Mjg2MA==' # 薪资 base64编码格式
workTime = "09:30" # 上班时间
closeTime = "18:30" # 下班时间
myHoliday = 8 # 月休息天数 考虑大小月
dailySalary = 130 # 日薪,如果设置则按日薪计算,格式为数字或 None
###############################
# 添加颜色输出支持
class Colors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
# 光标控制类
if os.name == 'nt':
class _CursorInfo(ctypes.Structure):
_fields_ = [("size", ctypes.c_int),
("visible", ctypes.c_byte)]
def hide_cursor() -> None:
"""隐藏控制台光标"""
if os.name == 'nt':
ci = _CursorInfo()
handle = ctypes.windll.kernel32.GetStdHandle(-11)
ctypes.windll.kernel32.GetConsoleCursorInfo(handle, ctypes.byref(ci))
ci.visible = False
ctypes.windll.kernel32.SetConsoleCursorInfo(handle, ctypes.byref(ci))
else:
print('\033[?25l', end='')
def show_cursor() -> None:
"""显示控制台光标"""
if os.name == 'nt':
ci = _CursorInfo()
handle = ctypes.windll.kernel32.GetStdHandle(-11)
ctypes.windll.kernel32.GetConsoleCursorInfo(handle, ctypes.byref(ci))
ci.visible = True
ctypes.windll.kernel32.SetConsoleCursorInfo(handle, ctypes.byref(ci))
else:
print('\033[?25h', end='')
today = datetime.now()
MINIMUM_WAGE = 22.2 # 广州最低时薪
class TimeFormat:
def sec2Text(self, sec: int) -> str:
"""将秒数转换为可读的时间文本"""
m, s = divmod(sec, 60)
h, m = divmod(m, 60)
return "%02d小时%02d分钟%02d秒" % (h, m, s)
def time2Sec(self, time_str: str) -> int:
"""将时间字符串转换为秒数"""
h, m, s = map(int, time_str.split(":"))
return h * 3600 + m * 60 + s
def getMonthDays(self, year: int, month: int) -> int:
"""获取指定年月的天数"""
if month in [1, 3, 5, 7, 8, 10, 12]:
return 31
elif month in [4, 6, 9, 11]:
return 30
elif (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
return 29
else:
return 28
def whatDay(self, date: datetime) -> dict:
"""判断日期类型"""
if holiday.is_workday(date.date()):
return {"type": 1, "name": "工作日"}
elif holiday.is_holiday(date.date()):
return {"type": 2, "name": "休息日"}
else:
return {"type": 1, "name": "工作日"} # 默认为工作日
def welcome() -> None:
"""显示欢迎信息"""
delta = today - datetime.strptime(entry, "%Y/%m/%d")
def time2Text() -> str:
if today.hour < 6:
return "凌晨好"
if 6 <= today.hour < 12:
return "早上好"
if 12 <= today.hour < 18:
return "下午好"
if 18 <= today.hour < 24:
return "晚上好"
msg = rf"""
___ ___ __ _____ _____ _ _
/ _ \ / _ \ / /|_ _/ ____| | | |
| (_) | (_) |/ /_ | || | | | | |
\__, |\__, | '_ \ | || | | | | |
/ / / /| (_) || || |____| |__| |
/_/ /_/ \___/_____\_____|\____/
{Colors.OKCYAN}{time2Text()}{Colors.ENDC}
{Colors.OKGREEN}今天是你上班的第{delta.days}天{Colors.ENDC}"""
print(msg)
time.sleep(3)
# 使用 ANSI 转义序列清屏而不是 cls 命令
print('\033[2J\033[H', end='')
def calculate_overtime_pay(wage: float, overtime_seconds: int, day_type: int) -> tuple:
"""计算加班费"""
overtime_hours = overtime_seconds / 3600
base_pay = (MINIMUM_WAGE if wage < MINIMUM_WAGE else wage) * overtime_hours
if day_type == 1: # 工作日
return base_pay * 1.5, "工作日加班费"
elif day_type == 2: # 休息日
return base_pay * 2, "休息日加班费"
else:
return base_pay * 1.5, "加班费" # 默认工作日
def loop() -> None:
"""主循环"""
tool = TimeFormat()
salary = int(base64.b64decode(mySalary).decode("utf-8")) # 薪资
start = timedelta(hours=int(workTime.split(
":")[0]), minutes=int(workTime.split(":")[1])) # 上班时间
end = timedelta(hours=int(closeTime.split(
":")[0]), minutes=int(closeTime.split(":")[1])) # 下班时间
offwork = False # 下班状态
last_overtime_display = 0 # 上次显示加班信息的时间
# 用于计算需要清除的行数
previous_lines = 0
while True:
now = datetime.now() # 当前时间
# 时薪:如果设置了日薪,则按日薪计算;否则按月工资收入÷(月计薪天数×8小时)计算
if dailySalary is not None:
myWage = dailySalary / 8 # 假设每天工作8小时
else:
myWage = salary/((tool.getMonthDays(now.year, now.month)-myHoliday)*8)
deltaNow = timedelta(
hours=now.hour, minutes=now.minute, seconds=now.second)
schedule = deltaNow - start # 当前进度
# 今日应得:如果设置了日薪,则按日薪计算;否则按月工资计算
if dailySalary is not None:
todayGet = dailySalary
else:
todayGet = salary/tool.getMonthDays(now.year, now.month)
todayProcess = schedule.seconds / \
(end-start).seconds if (end-start).seconds > 0 else 0 # 今日进度
if deltaNow >= start and deltaNow < end: # 上班时间内
progress_bar_length = 30
filled_length = int(progress_bar_length * todayProcess)
bar = '█' * filled_length + '-' * \
(progress_bar_length - filled_length)
msg = f"""
{Colors.OKBLUE}当前时间:{datetime.now().strftime("%Y/%m/%d %H:%M:%S")}{Colors.ENDC}
{Colors.OKGREEN}你已经上了:{(tool.sec2Text(schedule.seconds))}{Colors.ENDC}
{Colors.WARNING}还有 {tool.sec2Text((end-deltaNow).seconds)} 下班{Colors.ENDC}
今日工作进度: |{Colors.OKGREEN}{bar}{Colors.ENDC}| {todayProcess*100:.1f}%
今天已经挣了: {Colors.OKGREEN}{format(todayProcess*todayGet, '.2f')}{Colors.ENDC}元
还需努力挣: {Colors.WARNING}{format((1-todayProcess)*todayGet, '.2f')}{Colors.ENDC}元"""
# 计算当前消息的行数
current_lines = msg.count('\n')
# 清除之前的内容
if previous_lines > 0:
print(f'\033[{previous_lines}A\033[2K', end='')
for _ in range(current_lines - 1):
print('\033[2K\033[B', end='')
print(f'\033[{current_lines}A\033[2K', end='')
# 更新之前行数
previous_lines = current_lines
# 打印新内容
print(msg)
try:
time.sleep(1)
except KeyboardInterrupt:
print(f"\n{Colors.OKBLUE}再见!{Colors.ENDC}")
show_cursor()
exit()
elif deltaNow >= end:
dayType = tool.whatDay(now)
if not offwork:
offwork = True
msg = f"""
{Colors.OKGREEN} ____ ______ ________ ______ _____ _ __
/ __ \| ____| ____\ \ / / __ \| __ \| |/ /
| | | | |__ | |__ \ \ /\ / / | | | |__) | ' /
| | | | __| | __| \ \/ \/ /| | | | _ /| <
| |__| | | | | \ /\ / | |__| | | \ \| . \\
\____/|_| |_| \/ \/ \____/|_| \_\_|\_\\
{Colors.ENDC}
{Colors.OKCYAN} 下班啦!{Colors.ENDC}
"""
# 计算当前消息的行数
current_lines = msg.count('\n')
# 清除之前的内容
if previous_lines > 0:
print(f'\033[{previous_lines}A\033[2K', end='')
for _ in range(current_lines - 1):
print('\033[2K\033[B', end='')
print(f'\033[{current_lines}A\033[2K', end='')
# 更新之前行数
previous_lines = current_lines
# 打印新内容
print(msg)
try:
time.sleep(3)
except KeyboardInterrupt:
print(f"\n{Colors.OKBLUE}再见!{Colors.ENDC}")
show_cursor()
exit()
else:
msg = f"""
{Colors.OKBLUE}当前时间:{datetime.now().strftime("%Y/%m/%d %H:%M:%S")}{Colors.ENDC}
{Colors.OKGREEN}你已经上了:{(tool.sec2Text(schedule.seconds))}{Colors.ENDC}
{Colors.WARNING}已经下班 {tool.sec2Text(deltaNow.seconds - end.seconds)} 啦!{Colors.ENDC}
今天是:{Colors.OKCYAN}{dayType['name']}{Colors.ENDC}
"""
overtime = ((MINIMUM_WAGE if myWage < MINIMUM_WAGE else myWage) /
3600)*(deltaNow.seconds-end.seconds)
if dayType['type'] == 1:
overtime_pay, overtime_type = calculate_overtime_pay(
myWage, deltaNow.seconds - end.seconds, dayType['type'])
msg += f"\n{Colors.OKGREEN}{overtime_type}:{format(overtime_pay, '.2f')}元{Colors.ENDC}"
elif dayType['type'] == 2:
overtime_pay, overtime_type = calculate_overtime_pay(
myWage, deltaNow.seconds - end.seconds, dayType['type'])
msg += f"{Colors.OKGREEN}{overtime_type}:{format(overtime_pay, '.2f')}元{Colors.ENDC}"
# 法定节假日加班费按3倍计算
statutory_overtime = overtime * 3
msg += f"\n\n{Colors.WARNING}法定节假日加班费:{format(statutory_overtime, '.2f')}元{Colors.ENDC}"
# 计算当前消息的行数
current_lines = msg.count('\n')
# 清除之前的内容
if previous_lines > 0:
print(f'\033[{previous_lines}A\033[2K', end='')
for _ in range(current_lines - 1):
print('\033[2K\033[B', end='')
print(f'\033[{current_lines}A\033[2K', end='')
# 更新之前行数
previous_lines = current_lines
# 打印新内容
print(msg)
try:
time.sleep(1)
except KeyboardInterrupt:
print(f"\n{Colors.OKBLUE}再见!{Colors.ENDC}")
show_cursor()
exit()
else:
# 上班前时间
time_until_work = (start - deltaNow).seconds
msg = f"""
{Colors.OKBLUE}当前时间:{datetime.now().strftime("%Y/%m/%d %H:%M:%S")}{Colors.ENDC}
{Colors.WARNING}还未到上班时间{Colors.ENDC}
距离上班还有: {Colors.OKCYAN}{tool.sec2Text(time_until_work)}{Colors.ENDC}
今天是:{Colors.OKGREEN}{tool.whatDay(now)['name']}{Colors.ENDC}
"""
# 计算当前消息的行数
current_lines = msg.count('\n')
# 清除之前的内容
if previous_lines > 0:
print(f'\033[{previous_lines}A\033[2K', end='')
for _ in range(current_lines - 1):
print('\033[2K\033[B', end='')
print(f'\033[{current_lines}A\033[2K', end='')
# 更新之前行数
previous_lines = current_lines
# 打印新内容
print(msg)
try:
time.sleep(1)
except KeyboardInterrupt:
print(f"\n{Colors.OKBLUE}再见!{Colors.ENDC}")
show_cursor()
exit()
def main() -> None:
"""主函数"""
hide_cursor() # 隐藏光标
welcome()
loop()
if __name__ == "__main__":
try:
main()
finally:
show_cursor() # 显示光标
# 清屏
print('\033[2J\033[H', end='')
在代码中,我们实现了上班欢迎语,下班提示语,以及薪资的计算。
启动脚本后,我们就能看到今天是当牛马的第xx天?
也能根据当前时间判断早上好中午好晚上好。

其中,配置区我们配置了入职日期,base64编码的工资(以免有人看到心生嫉妒),上下班时间,以及月休息天。实际脚本运行中,还考虑了大小月(28天/30天/31天)的问题,让秒薪计算的更加精确。
欢迎语结束后实际运行效果如下:

真是越来越有盼头了!
下班后会有大横幅提示。

接下来重头戏!
脚本使用了chinese_calendar库判断当前是否为国内周末休息日、调休以及法定节假日,并按照设定的当地最低日薪计算当前的加班费。如果公司有加班费的相关补贴还好,不然就是实际亏损的薪资!?
(看看谁没有加班费~)

脚本所有功能介绍完毕,希望能帮到你。
发表回复