C语言代码把时间戳字符串转换成日期时间格式以及修正bug的测试方法

时间戳是一种用来表示日期和时间的数字格式,在不同的编程语言里时间戳的长度和单位都不一样:

C:以秒为单位,目前的时间戳是10位数。

Python:以秒为单位并且有精确到7位小数的毫秒,目前的时间戳整数部分是10位数,毫秒是7位小数。

JavaScript:以毫秒为单位,目前的时间戳是13位数。

虽然时间戳在计算机内部处理时间非常方便,但对于人类来说显得不直观。在日常编程工作中经常遇到需要将时间戳转换为日期时间格式方便,有以下好处:

 1. 可读性:将时间戳转换为日期时间格式后,时间数据变得更容易理解。这对于用户界面、日志记录和数据可视化非常重要。

2. 数据处理:日期时间格式允许我们执行各种时间相关的操作,如排序、筛选和计算时间间隔。

3. 报告和分析:日期时间格式更容易传达时间信息,使数据更易于解释。

使用C语言的 time.h 库

在C语言中使用 time.h 库来执行时间戳字符串到日期时间格式的转换。下面是演示代码,使用localtime函数将时间戳转换为 tm 结构,然后使用 sprintf 将其格式化为日期时间字符串。

char* timestamp_to_datetime(long long timestamp){struct tm *tm;time_t time_seconds;tm = localtime(&time_seconds);// 容错处理if (tm == NULL) {perror("localtime");return NULL;}char* result=malloc(25);sprintf(result, "%04d-%02d-%02d %02d:%02d:%02d",tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,tm->tm_hour, tm->tm_min, tm->tm_sec);return result;    
}

我的设想是:对于像JavaScript产生的13位数时间戳,先处理前10位数转换日期时间,然后把末尾的3位数作为毫秒数添加到日期时间里。小于13位数的时间戳则直接转换。因此需要判断时间戳字符串长度,该函数修改如下:

char* timestamp_to_datetime(long long timestamp){int timestamp_length = snprintf(NULL, 0, "%lld", timestamp);struct tm *tm;time_t time_seconds;// 如果时间戳长度为13,则取前10位数为时间戳time_seconds = timestamp_length == 13 ? (time_t)(timestamp / 1000) : (time_t)(timestamp);tm = localtime(&time_seconds);// 容错处理if (tm == NULL) {perror("localtime");return NULL;}// 时间戳的前10位数转换为日期和时间char* result=malloc(25);sprintf(result, "%04d-%02d-%02d %02d:%02d:%02d",tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,tm->tm_hour, tm->tm_min, tm->tm_sec);// 如果时间戳长度为13,则将11~13位数作为毫秒数if (timestamp_length == 13) {char ms[5];  // Make sure to have space for the null terminatorsprintf(ms, ".%03lld", timestamp % 1000);strcat(result, ms);}return result;    
}

接下来是主程序的代码,接收命令行输入的第一个参数作为时间戳字符串:

int main(int argc, char *argv[]) {long long timestamp;char* result=malloc(25);if (argc != 2) {printf("\n时间戳转换成日期时间。\n\n使用方法:%s <时间戳>\n\n注:时间戳长度不应该超过13位数字。\n", argv[0]);// 获取当前时间time(&timestamp);printf("\n当前时间戳:%lld\n", timestamp);result = timestamp_to_datetime(timestamp);printf("时间戳转换为日期时间是:%s\n", result);free(result);return 1;}// 从命令行参数获取第一个参数作为时间戳字符串char *timestamp_str = argv[1];int timestamp_length = strlen(timestamp_str);timestamp = atoll(timestamp_str); printf("你输入的时间戳是:%lld\n长度为%d位数。\n", timestamp, timestamp_length);result = timestamp_to_datetime(timestamp);if (result != NULL){printf("时间戳转换为日期时间是:%s\n", result);free(result);}return 0;
}

程序基本上写好了。但这个代码存在多出bug:

Bug #1:输入参数的合法性

首先没有检查输入参数是否合法,如输入的字符串必须是数字才行,如果输入的参数是混有字母或其它符号,localtime 函数转换不了会直接报错退出程序。

在Python里有检查一个字符串是否全是数字的方法:string.isdigit()。该方法简单有效。C语言虽然也有 isdigit(),但是它只负责检查一个字符是否数字而不是判断字符串,而C语言的库里没有现成的函数检查一个字符串是否数字,所以只好手写一个:

// 检查字符串是否全是数字
int isStringAllDigits(const char *str) {for (int i = 0; str[i] != '\0'; i++) {if (!isdigit((unsigned char)str[i])) {return 0; // 只要检测到非数字的字符串就直接返回0}}return 1; // 全是数字的话返回1
}

或者写得高级一些,使用指针和while循环,更简练高效:

// 检查字符串是否全是数字,高级写法:
int isStringAllDigits(const char *str) {while (*str)if (!isdigit((unsigned char)(*str++)))return 0; return 1;
}

Bug #2:检查输入参数边界的有效性。

输入的时间戳字符串必须限制不超过13位数。

    if (timestamp_length > 13) {printf("长度不正确!注:时间戳长度不应该超过13位数字。\n\n");return 1;}

编译后运行测试,发现当时间戳是11、12位数大数字情况下,会返回 localtime 报错信息,数值越过边界了。

查阅相关文档(localtime、_localtime32、_localtime64 | Microsoft Learn),得知:localtime 返回的最大时间是 3000年12月31日 23:59:59,那么对应的时间戳是32535158399。

但是经过我调试,时间戳大于32535158399仍可以输出日期时间:

只要时间戳长度小于13,时间戳的最大值不能超过 32536799999,日期最大值可以到达3001年1月19日15:59:59。

因此需要加一段检查参数是否越过边界的代码:

    if (timestamp>32536799999 && timestamp<1000000000000){printf("注:%d位数的数值不能大于32536799999。\n你可以尝试输入13位数的时间戳。", timestamp_length);return 1;}

Bug #3:输入参数的确是数字,但却是以0开头的长串数字。

这个情况很容易被忽视,假如输入的是0开头的数字:001、0123456789,就需要先出去多余的0。

要先转换字符串,从头开始检查非零的位置,然后截取到末尾,最后转成数字吗?

没这么麻烦,C语言已提供一系列灵活的转换函数当中就有:atoll(),把字符串格式化成长整数类型变量:

    timestamp = atoll(timestamp_str); 

这下就把前面多余的0清除掉了。但有个问题:

假如输入的是很长的0开头的数字,如:00000000000009876543210,

timestamp变量经过 atoll(timestamp_str) 转换后是9876543210。

timestamp_str 字符串储存了“00000000000009876543210”,长度为23。

如果判断timestamp_str的长度是否不超过13,那按照上面代码的判断,将直接输出:长度超过13,程序退出。

所以应该判断 timestamp 的长度。

由于timestamp的类型是long long长整数型,不能直接使用 strlen(timestamp) 来获取其长度,只能另外声明一个临时字符串,比如 formatted_str 来存储 timestamp 转换成字符串,最后判断 formatted_str 的长度:

    // 如果输入值为0开头,如:0000123456789,则必须先用atoll转成长数值,去除前面所有0timestamp = atoll(timestamp_str);   // 去除前面多余的0之后,用snprintf格式化并存入formatted_strchar formatted_str[20];snprintf(formatted_str, sizeof(formatted_str), "%llu", timestamp);// 判断有效的时间戳字符串长度,不要拿timestamp_str而应该拿formatted_str来判断;int timestamp_length = strlen(formatted_str);printf("你输入的时间戳是:%lld\n长度为%d位数。\n", timestamp, timestamp_length);if (timestamp_length > 13) {printf("长度不正确!注:时间戳长度不应该超过13位数字。\n\n");return 1;}

这样一来,即使输入长长的数字也能妥善处理,不至于返回 localtime 的错误提示。

Bug #4:输出的代码页问题

C代码编译运行的.exe程序,默认以UTF-8格式输出文字。UTF-8对应的代码页为65001。

而Windows系统的命令行的默认格式是GBK,代码页为936。

上述的代码编译运行在Windows的命令行里,所有中文会显示成乱码。为了适应Windows的命令行,C代码里应该在主程序加一行设定代码页为UTF-8,以确保输出的文字正确显示。

#include <windows.h>。。。int main(int argc, char *argv[]) {// 切换至UTF-8(65001)环境输出if (GetConsoleOutputCP() != CP_UTF8)SetConsoleOutputCP(CP_UTF8); 。。。
}

好了,经过上面检查输入合法性、检查变量边界,代码的bug基本上修复好了,下面是完整的代码。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <windows.h>// 检查字符串是否全是数字
int isStringAllDigits(const char *str) {while (*str)if (!isdigit((unsigned char)(*str++)))return 0; return 1;
}char* timestamp_to_datetime(long long timestamp){int timestamp_length = snprintf(NULL, 0, "%lld", timestamp);struct tm *tm;time_t time_seconds;// 如果时间戳长度为13,则取前10位数为时间戳time_seconds = timestamp_length == 13 ? (time_t)(timestamp / 1000) : (time_t)(timestamp);tm = localtime(&time_seconds);// 容错处理if (tm == NULL) {perror("localtime");return NULL;}// 时间戳的前10位数转换为日期和时间char* result=malloc(25);sprintf(result, "%04d-%02d-%02d %02d:%02d:%02d",tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,tm->tm_hour, tm->tm_min, tm->tm_sec);// 如果时间戳长度为13,则将11~13位数作为毫秒数if (timestamp_length == 13) {char ms[5];sprintf(ms, ".%03lld", timestamp % 1000);strcat(result, ms);}return result;    
}int main(int argc, char *argv[]) {// 切换至UTF-8(65001)环境输出if (GetConsoleOutputCP() != CP_UTF8) SetConsoleOutputCP(CP_UTF8); long long timestamp;char* result=malloc(25);if (argc != 2) {printf("\n时间戳转换成日期时间。\n\n使用方法:%s <时间戳>\n\n注:时间戳长度不应该超过13位数字。\n", argv[0]);// 获取当前时间time(&timestamp);printf("\n当前时间戳:%lld\n", timestamp);result = timestamp_to_datetime(timestamp);printf("时间戳转换为日期时间是:%s\n", result);free(result);return 1;}// 从命令行参数获取第一个参数作为时间戳字符串char *timestamp_str = argv[1];// 判断输入参数是否全是数字if (!isStringAllDigits(timestamp_str)) {printf("输入不合法。请输入由数字组成的时间戳。\n");return 1;}// 如果输入值为0开头,如:0000123456789,则必须先用atoll转成长数值,去除前面所有0timestamp = atoll(timestamp_str);   // 去除前面多余的0之后,用snprintf格式化并存入formatted_strchar formatted_str[20];snprintf(formatted_str, sizeof(formatted_str), "%llu", timestamp);// 判断有效的时间戳字符串长度,不要拿timestamp_str而应该拿formatted_str来判断;int timestamp_length = strlen(formatted_str);printf("你输入的时间戳是:%lld\n长度为%d位数。\n", timestamp, timestamp_length);if (timestamp_length > 13) {printf("长度不正确!注:时间戳长度不应该超过13位数字。\n\n");return 1;}if (timestamp>32536799999 && timestamp<1000000000000){printf("注:%d位数的数值不能大于32536799999。\n你可以尝试输入13位数的时间戳。\n", timestamp_length);return 1;}result = timestamp_to_datetime(timestamp);if (result != NULL){printf("时间戳转换为日期时间是:%s\n", result);free(result);}return 0;
}

运行结果截图:

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

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

相关文章

基于springboot小区团购管理系统

基于springboot小区团购管理系统的设计与实现 摘要 小区团购管理系统是一款基于Spring Boot框架的Web应用&#xff0c;为小区居民提供了一个方便的平台&#xff0c;以协调和管理各种团购活动。该系统的主要目标是促进小区居民之间的互助合作&#xff0c;通过集中采购来降低商品…

Ubuntu 22.04 中安装 fcitx5

Ubuntu 22.04 中安装 fcitx5 可以按照以下步骤进行&#xff1a; 添加 fcitx5 的 PPA 首先&#xff0c;添加 fcitx5 的官方 PPA&#xff1a; sudo add-apt-repository ppa:fcitx-team/fcitx5更新软件包列表 sudo apt update安装 fcitx5 sudo apt install fcitx5 fcitx5-conf…

【JavaEE初阶】 CAS详解

文章目录 &#x1f332;什么是 CAS&#x1f6a9;CAS伪代码 &#x1f38b;CAS 是怎么实现的&#x1f333;CAS的应用&#x1f6a9;实现原子类&#x1f6a9;实现自旋锁 &#x1f384;CAS 的 ABA 问题&#x1f6a9;什么是 ABA 问题&#x1f6a9;ABA 问题引来的 BUG&#x1f6a9;解决…

Mac安装nginx(Homebrew)

文章目录 nginx 安装nginx 反向代理nginx 反向代理配置nginx 负载均衡配置 nginx 安装 查看需要安装 nginx 的信息 brew info nginxDocroot 默认为 /usr/local/var/www 在 /opt/homebrew/etc/nginx/nginx.conf 配置文件中默认端口被配置为8080&#xff0c;从而使 nginx 运行…

常用Win32 API的简单介绍

目录 前言&#xff1a; 控制控制台程序窗口的指令&#xff1a; system函数&#xff1a; COORD函数&#xff1a; GetStdHandle函数&#xff1a; GetConsoleCursorInfo函数&#xff1a; CONSOLE_CURSOR_INFO函数&#xff1a; SetConsoleCursorInfo函数&#xff1a; SetC…

Vue 实战项目(智慧商城项目): 完整的订单购物管理功能 内涵资源代码 基于Vant组件库 Vuex态管理 基于企业级项目开发规范

鹏鹏老师的实战开发项目 智慧商城项目 接口文档&#xff1a;安全问题&#xff08;需要私信即可&#xff09; 演示地址&#xff1a;跳转项目地址 01. 项目功能演示 1.明确功能模块 启动准备好的代码&#xff0c;演示移动端面经内容&#xff0c;明确功能模块 在这里插入图…

DevExpress WinForms甘特图组件 - 轻松集成项目管理功能到应用

DevExpress WinForms Gantt&#xff08;甘特图&#xff09;控件允许您在下一个WinForms桌面应用程序中快速合并项目规划和任务调度功能。 DevExpress WinForms有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。同时能完美构建流畅、美观且易于…

【超参数研究02】使用随机搜索优化超参数

一、说明 在神经网络训练中&#xff0c;超参数也是需要优化的&#xff0c;然而在超参数较多&#xff08;大于3个&#xff09;后&#xff0c;如果用穷举的&#xff0c;或是通过经验约摸实现就显得费时费力&#xff0c;无论如何&#xff0c;这是需要研究、规范、整合的要点&#…

Banana Pi BPI-M4 Berry 采用全志H618芯片,板载2G RAM和8G eMMC

BPI-M4 Berry 开发板作为一款强大的单板计算机&#xff08;SBC&#xff09;&#xff0c;充分挖掘了全志 H618 系统级芯片&#xff08;SoC&#xff09;的功能&#xff0c;为开发人员提供了令人印象深刻的性能和丰富的特性。与树莓派 4b 类似&#xff0c;BPI-M4 Berry 能够展现与…

网站页脚展示备案号并在新标签页中打开超链接

备案时&#xff0c;我们就注意到&#xff0c;备案成功后需要在网站首页底部展示“备案号”&#xff0c;并将备案号链接至https://beian.miit.gov.cn。 这里我使用了WrodPress中的主题&#xff0c;主题自定义中有提供对页脚文本的编辑&#xff0c;支持用css标签定义样式。若是自…

MySQL MVCC机制探秘:数据一致性与并发处理的完美结合,助你成为数据库高手

一、前言 在分析 MVCC 的原理之前&#xff0c;我们先回顾一下 MySQL 的一些内容以及关于 MVCC 的一些简单介绍。&#xff08;注:下面没有特别说明默认 MySQL 的引擎为 InnoDB &#xff09; 1.1 数据库的并发场景 数据库并发场景有三种&#xff0c;分别是&#xff1a; 读-读…

基于springboot实现广场舞团平台系统项目【项目源码+论文说明】计算机毕业设计

基于SPRINGBOOT实现广场舞团平台系统演示 摘要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&am…

算法通关村第十一关青铜挑战——移位运算详解

大家好&#xff0c;我是怒码少年小码。 计算机到底是怎么处理数字的&#xff1f; 数字在计算机中的表示 机器数 一个数在计算机中的二进制表示形式&#xff0c;叫做这个数的机器数。 机器数是带符号的&#xff0c;在计算机用一个数的最高位存放符号&#xff0c;正数为0&am…

Unity之ShaderGraph如何实现全息投影效果

前言 今天我们来实现一个全息投影的效果&#xff0c;如下所示&#xff1a; 主要节点 Position&#xff1a;提供对网格顶点或片段的Position 的访问&#xff0c;具体取决于节点所属图形部分的有效着色器阶段。使用Space下拉参数选择输出值的坐标空间。 Time&#xff1a;提…

C++入门(3):引用,内联函数

一、引用 1.1 引用特性 引用必须初始化 一个变量可以有多个引用 引用一旦引用一个实体&#xff0c;就不能引用其他实体 int main() {int a 10, C 20;int& b a;b c; // 赋值&#xff1f;还是b变成c的别名&#xff1f;return 0; }1.2 常引用 引用权限可以平移或缩小…

ubuntu双系统安装以及启动时卡死解决办法

目录 一.简介 二.安装 如何安装Ubuntu20.04(详细图文教程-CSDN博客 Ubuntu22.04&#xff08;非虚拟机&#xff09;安装教程&#xff08;2023最新最详细&#xff09;-CSDN博客 三.ubuntu双系统启动时卡死解决办法&#xff08;在ubuntu16.04和18.04测试无误&#xff09; 问题…

vue实现响应式改变scss样式

需求&#xff1a;侧边导航栏点击收起&#xff0c;再次点击展开&#xff0c;但是我这个项目的位置是在左侧菜单栏所以需要自定义 效果图&#xff1a; 实现步骤&#xff1a; 1&#xff1a;定义一个变量&#xff08;因为我这里会存储菜单栏的状态所以需要存储状态&#xff0c;一…

09、Python 字典入门 及 高级用法

目录 字典创建字典通过key访问value添加key-value对删除key-value对替换key-value对 判断是否包含指定keydict与列表字典的常用方法演示&#xff1a; 用字典格式化字符串 创建字典 操作字典key-value对 理解dict与list的关系 字典常用方法 使用字典格式化字符串 字典 字典用于…

2023/10/23 mysql学习

数据库修改 show databases; 展示所有数据库 create database 数据库名; 创建数据库 create database if not exists 数据库名; 如果未创建过当前数据库名则创建 drop database 数据库名; drop database if exists 数据库名;用法和创建类似 删除数据库 use 数据库名; 跳…

分享一下商城小程序怎么设置分销功能

随着互联网的快速发展&#xff0c;传统的营销方式已经无法满足企业的需求。在这个时代&#xff0c;拥有一个高效的分销系统已经成为了企业成功的关键之一。而商城小程序作为近年来火爆的电商新模式&#xff0c;其中的分销功能更是备受关注。本文将以分销功能为主要主题&#xf…