-
代码思路都是抄这篇文章的代码【C语言】UTC时间转换为北京时间, 在了解思路之前,首先我们得知道什么是时区简单的理解就是时间的分区,本初子午线为标准时(即+0),比如:中国时区为+8则 时区为东八区,美国加州时区为-8则,时区为西八区,世界上有26个时区,最大的东十四区(基里巴斯时间),最小的是西十二区(豪兰岛时间)。接下来了解一下时差,什么是时差呢?即不同时区的差别,例如:标准时与北京时间相差了8个小时,纽约时间与北京时间相差了12个小时,还有一个时间戳的概念,时间戳指1970-01-01 08:00:00到当前时间有多少秒即时间戳。
-
代码思路:
–> 第一步: 定义一个时间变量和时间结构体指针两个(不喜欢指针也可以用变量),分别是GMT标准时结构体和本地时间结构体。
–> 第二步: 使用time()函数获取时间戳,初始化标准时结构体和本地时结构体为空(不初始化也行),使用gmtime()获取标准时,将标准时的各种信息传递给标准时结构体。
–> 第三步: 接下来的核心代码就有点多了,请大家认真看完
-
函数返回值: 一个时间结构体指针,参数(标准时时间结构体, 时区时, 时区分 (定义时区分用来处理半时区的城市) )
-
首先定义年、月、年天、月天、星天、时、分,在定义两个变量来记录上个月的最大的总天数。
-
定义一个静态标准时结构体变量(记得别定义局部变量).
-
初始化
年份 = 标准时的年份;
月份 = 标准时的月份;
月天 = 标准时的月天;
星天 = 标准时的星天;
年天 = 标准时的年天;
小时 = 标准时的小时 + 时区时;
分钟 = 标准时的分钟 + 时区分; -
(月份的判断)1月大,2月小,3月大,4月小,5月大,6月小,7月大,8月大,9月小,10月大,11月小,12月大
如果 是大月,当月最大天数=31天,上个月最大天数=30天
其他如果 3月份的上个总天数,闰年=29天,平年=28天
其他如果 1月份的上个月总天数=31天,8月份上个月总天数=31天
其他如果 是小月,当月最大天数为=30天,上个月最大天数为=31天
其他
上个月最大天数=31天
如果 3月份的上个总天数,闰年=29天,平年=28天
- (时间的判断) 有东时区(为正数)和西时区(为负数)之分,时区有分半时区和全时区
1. 要判断是否为东时区
如果 时区时 >= 0
如果 标准时+时区分 >= 60
分钟 减等于 60 /* 因为分钟这个变量是标准时分钟+时区分算出来结果,不能直接等于0 */
小时 加等于 01
如果 小时 >= 24
小时 减等于 24
月天 加等于 01 /* 月份中第几天 */
星天 加等于 01 /* 代表着星期几 */
年天 加等于 01 /* 一年中第几天 */
如果 星天 > 06
星天 减等于 07
如果 月天 > 当月最大天数
月天 减等于 当月最大天数
月加 加等于 01
如果 月份 > 12
月份 减等于 12
年份 加等于 01
如果 年天 > 当前年最大天数
年天 等于 0 /* 为什么等于0呢,因为这是外国人习惯都是以0天开始为第一天 */
/* 如果非半时区,分钟没达到上限,直接对小时上限做调整(无上限不做修改) */
如果 小时 >= 24
小时 减等于 24
月天 加等于 01 /* 月份中第几天 */
星天 加等于 01 /* 代表着星期几 */
年天 加等于 01 /* 一年中第几天 */
如果 星天 > 06
星天 减等于 07
如果 月天 > 当月最大天数
月天 减等于 当月最大天数
月加 加等于 01
如果 月份 > 12
月份 减等于 12
年份 加等于 01
如果 年天 > 当前年最大天数
年天 等于 0 /* 为什么等于0呢,因为这是外国人习惯都是以0天开始为第一天 */
其他
如果 时区分 == 0 并且 时区时 < 0
如果 小时 < 0
小时 加等于 24
月天 减等于 01 /* 月份中第几天 */
星天 减等于 01 /* 代表着星期几 */
年天 减等于 01 /* 一年中第几天 */
如果 星天 < 0
星天 等于 06
如果 月天 < 01
月天 等于 上个月最大天数
月加 减等于 01
如果 月份 < 当月最大天数
月份 等于 12
年份 减等于 01
如果 年天 < 0
年天 等于 当前年最大天数 /* 为什么等于0呢,因为这是外国人习惯都是以0天开始为第一天 */
其他
如果 分钟 < 0
分钟 加等于 60
小时 减等于 01
如果 小时 < 0
小时 加等于 24
月天 减等于 01 /* 月份中第几天 */
星天 减等于 01 /* 代表着星期几 */
年天 减等于 01 /* 一年中第几天 */
如果 星天 < 0
星天 等于 6
如果 月天 < 1
月天 等于 上个月的最大天数
月份 减等于 01
如果 月份 < 当月最大天数
月份 等于 12
年份 减等于 01
如果 年天 < 0
年天 等于 当前年最大天数
如果 小时 < 0 并且 时区分 != 0
小时 加等于 24
月天 减等于 01 /* 月份中第几天 */
星天 减等于 01 /* 代表着星期几 */
年天 减等于 01 /* 一年中第几天 */
如果 星天 < 0
星天 等于 6
如果 月天 < 1
月天 等于 上个月的最大天数
月份 减等于 01
如果 月份 < 当月最大天数
月份 等于 12
年份 减等于 01
如果 年天 < 0
年天 等于 当前年最大天数
最后使用浅复制的方法把修改后的数据复制过去.
最后返回这个块静态内存.
___________________________________________________________________________
3. 接下来是用之前的代码修改后的函数(这是旧代码就留着吧)
#include <stdio.h>
#include <stdlib.h> /* _sleep(), system() */
#include <conio.h> /* _kbhit() */
#include <time.h>
/*
* 最大的属性值.
*/
enum MAX_ATTR
{
MAX_MINUTE = 0X3C, /* 分钟的区间[0, 59] */
MAX_HOUR = 0X18, /* 小时的区间[0, 23] */
MAX_WEEK = 0X07, /* 工作日的区间[0, 6] */
MAX_MDAY = 0X1F, /* 大月天数的区间[1, 31] */
MIN_MDAY = 0X1E, /* 小月天数的区间[1, 30] */
LEAP_DAY = 0X1D, /* 二月份的最大天数(闰年)[0, 29] */
COMM_DAY = 0X1C, /* 二月份的最小天数(平年)[0, 28] */
MAX_MONTH = 0X0C /* 月份的区间[0, 11] */
};
/*
* 判断是闰年还是平年
* 参数说明: __year年份
* 返回值: 1为真,0为假
*/
static int is_leap(int __year)
{
int four = __year % 4;
int hundred = __year % 100; /* 取余数 */
int fourhundred = __year % 400;
if (four == 0 && hundred == 0 && fourhundred == 0)
{
return (1);
}
else if (four == 0 && hundred != 0 && fourhundred != 0)
{
return (1);
}
else
{
return (0);
}
}
/*
* 获取一年的最大天数
* 参数说明: __year年份
* 返回值: 返回年份的最大天数
*/
static int max_yearday(int __year)
{
if (is_leap(__year)) {
return (366 - 1); /* 闰年 [0, 365] */
} else {
return (365 - 1); /* 平年 [0, 364] */
}
}
/*
* 函数说明: 将标准时转为本地时
* 参数说明: __utc标准时结构体, __timezone_hour时区时, __timezone_min时区分(处理半时区而来的)
* 返回值: 返回本地时间结构体
*/
struct tm* utc_to_local(struct tm* __utc, int __timezone_hour, int __timezone_min)
{
int year, month, mday, wday, yday, hour, minute;
/* 当月日达到上限是要更换月份,月日也会被重置,所以要记录当月最大天数和上个月的最大天数 */
int lastday = 0; /* 当前月最大天数 */
int lastday_of_lastmonth = 0; /* 上个月最大天数 */
static struct tm __local = { 0 }; /* 本地时间结构体 */
year = __utc->tm_year;
month = __utc->tm_mon ;
mday = __utc->tm_mday;
wday = __utc->tm_wday;
yday = __utc->tm_yday;
hour = __utc->tm_hour + __timezone_hour;
minute = __utc->tm_min + __timezone_min ;
switch (month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
lastday = MAX_MDAY; /* 大月的总天数 */
lastday_of_lastmonth = COMM_DAY; /* 大月上个月的总天数 */
/* 判断三月份的上一个月的总天数 */
switch (month)
{
case 3:
/* 判断二月份是否闰年(二月份的最大总天数) */
if (is_leap(year)) {
lastday_of_lastmonth = LEAP_DAY;
}
else {
lastday_of_lastmonth = COMM_DAY;
}
break;
}
/*
* 有些大月的上一个月的总天数是一样的, 比如8月上一个月也是31天.
* 1月的上一个月也是31天,记录连续的大月
*/
switch (month)
{
case 1:
case 8:
lastday_of_lastmonth = MAX_MDAY;
break;
}
break;
case 4:
case 6:
case 9:
case 11:
lastday = COMM_DAY;
lastday_of_lastmonth = MAX_MDAY;
break;
default:
lastday_of_lastmonth = MAX_MDAY;
/* 判断二月份是否闰年 */
if (is_leap(year)) {
lastday_of_lastmonth = LEAP_DAY;
}
else {
lastday_of_lastmonth = COMM_DAY;
}
break;
}
}
/* 对时区按条件进行调整(如果是东时区) */
if ( __timezone_hour >= 0 )
{
if (minute >= MAX_MINUTE)
{
minute -= MAX_MINUTE; /* 分 */
hour += 1;
if (hour >= MAX_HOUR)
{
hour -= MAX_HOUR; /* 时 */
mday += 1; /* 月日 */
wday += 1; /* 星日 */
yday += 1; /* 年日 */
if (wday > 6)
{
wday -= MAX_WEEK;
}
if (mday > lastday)
{
mday -= lastday;
month += 1;
if (month >= MAX_MONTH)
{
month -= MAX_MONTH;
year += 1;
}
}
if (yday > max_yearday(year))
{
yday = 0;
}
}
}
/* 如果非半时区,分钟没达到上限,直接对小时上限做调整(无上限不做修改) */
if (hour >= MAX_HOUR)
{
hour -= MAX_HOUR;
mday += 1;
wday += 1;
yday += 1;
if (wday > 6)
{
wday -= MAX_WEEK;
}
if (mday > lastday)
{
mday -= lastday;
month += 1;
if (month >= MAX_MONTH)
{
month -= MAX_MONTH;
year += 1;
}
}
if (yday > max_yearday(year))
{
yday = 0;
}
}
}
else
{
/* 对时区按条件进行调整(如果是西时区的半时区) */
if (__timezone_min == 0 && __timezone_hour < 0)
{
if (hour < 0)
{
hour += MAX_HOUR;
mday -= 1;
wday -= 1;
yday -= 1;
if (wday < 0)
{
wday = 6;
}
if (mday < 1)
{
mday = lastday_of_lastmonth;
month -= 1;
if (month < lastday)
{
month = MAX_MONTH;
year -= 1;
}
}
if (yday < 0)
{
yday = max_yearday(year);
}
}
else
{
if (minute < 0)
{
minute += MAX_MINUTE;
hour -= 1;
if (hour < 0)
{
hour += MAX_HOUR;
mday -= 1;
wday -= 1;
yday -= 1;
if (wday < 0)
{
wday = 6;
}
if (mday < 1)
{
mday = lastday_of_lastmonth;
month -= 1;
if (month < lastday)
{
month = MAX_MONTH;
year -= 1;
}
}
if (yday < 0)
{
yday = max_yearday(year);
}
}
}
/* 当分钟不小于0时,直接处理对小时的上限 */
if (hour < 0 && __timezone_min != 0)
{
hour += MAX_HOUR;
mday -= 1;
wday -= 1;
yday -= 1;
if (wday < 0)
{
wday = 6;
}
if (mday < 1)
{
mday = lastday_of_lastmonth;
month -= 1;
if (month < lastday)
{
month = MAX_MONTH;
year -= 1;
}
}
if (yday < 0)
{
yday = max_yearday(year);
}
}
}
}
__local.tm_year = year;
__local.tm_mon = month;
__local.tm_mday = mday;
__local.tm_wday = wday;
__local.tm_yday = yday;
__local.tm_hour = hour;
__local.tm_min = minute;
__local.tm_sec = __utc->tm_sec;
__local.tm_isdst = __utc->tm_isdst;
return &__local;
}
int main(int argc, char* argv[])
{
time_t __time;
struct tm* __local = NULL, * __gmt = NULL;
system("cls");
system("mode con cols=64 lines=2");
while (!_kbhit())
{
time(&__time);
__gmt = gmtime(&__time);
__local = utc_to_local(__gmt, -12, 00);
printf("%02d-%02d-%02d %02d:%02d:%02d 星期%02d %02d的第%02d天\n",
__local->tm_year + 1900, __local->tm_mon + 1, __local->tm_mday,
__local->tm_hour, __local->tm_min, __local->tm_sec,
__local->tm_wday, __local->tm_year + 1900, __local->tm_yday + 1
);
_sleep(500);
}
return 0;
}
- 由于上面的旧代码功能不模块化,新代码进行了模块化提高阅读效率,另外加入的新函数mktime_Ymdhms,把年月日时分秒转为时间戳,另外mktime_Firday函数可以自己定义一个,博主这里就懒得写了很简单的!
libtm.h
/*
* libtm.h
*
* The C Language time base function extension
*/
#pragma once
#ifndef _LIB_TM_H
#define _LIB_TM_H
/* month tag and month day max */
enum month
{
Jan, Feb, Mar, Apr, May, Jun,
Jul, Aug, Sep, Oct, Nov, Dec
};
enum monthDay
{
NLEAP_MONTH = 28, LEAP_MONTH,
LUNAR_MONTH, SOLAR_MONTH
};
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
/* Time zone offset from Greenwich */
struct tz {
int tz_hour;
int tz_min;
};
/* utc timezone to any timezone */
extern struct tm* utc_to_local(struct tm* tm, struct tz tz);
/* make time to this format(year, mon, mday, hour, min, sec)*/
extern time_t mktime_Ymdhms(int year, int mon, int mday,
int hour, int min, int sec);
#ifdef __cplusplus
time_t mktime(int year, int mon, int mday,
int hour, int min, int sec)
{
return mktime_Ymdhms(year, mon, mday, hour, min, sec);
}
}
#endif
utc_to_local.c
#include "libtm.h"
// solar month 31days
#define SOLAR_MONTH 31
// lunar month 30days
#define LUNAR_MONTH 30
// leap year Feb month is 29days
#define LEAP_MONTH 29
// noleap year Feb month is 28days
#define NLEA_MONTH 28
// Maximum value of time unit
#define MAX_HOUR 24
#define MAX_MIN 60
#define MAX_WEE 07
#define MAX_MON 12
// Total number of days in this month, total number of days in the previous month
// month day
struct md {
int md_lastday, md_lastday_of_lastmon;
};
/* time buffer used to store parameter return values */
struct tm _tmbuf;
enum month
{
Jan, Feb, Mar, Apr, May, Jun,
Jul, Aug, Sep, Oct, Nov, Dec
};
static int isleap(int year)
{
return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? 1 : 0;
}
// 364 no leap year day
// 365 is leap year day
static int max_yday(int year)
{
return isleap(year) ? (366 - 1) : (365 - 1);
}
static struct tm tm_convert(struct tm* tp, struct tz tz)
{
static struct tm tm = { 0 }; // variable returned after
tm.tm_year = tp->tm_year;
tm.tm_mon = tp->tm_mon;
tm.tm_yday = tp->tm_yday;
tm.tm_mday = tp->tm_mday;
tm.tm_wday = tp->tm_wday;
tm.tm_isdst = tp->tm_isdst;
// The location is running summer time, and needs to be offset for 1 hour
if (tm.tm_isdst != 0 && tm.tm_isdst != -1)
{
tm.tm_hour = tp->tm_hour + tz.tz_hour + tp->tm_isdst;
}
else
{
tm.tm_hour = tp->tm_hour + tz.tz_hour;
}
tm.tm_min = tp->tm_min + tz.tz_min;
tm.tm_sec = tp->tm_sec;
#if __linux || __linux__
tm.tm_gmtoff = tp->tm_gmtoff;
tm.tm_zone = tp->tm_zone;
#endif
return tm;
}
static struct md get_monthday(struct tm tm)
{
static struct md md = { 0 };
switch (tm.tm_mon)
{
/* solar month date processing */
case Jan:
case Mar:
case May:
case Jul:
case Aug:
case Oct:
case Dec:
// lastday is 31
// lastday of lastmonth is 30
md.md_lastday = SOLAR_MONTH;
md.md_lastday_of_lastmon = LUNAR_MONTH;
// lastdays February
if (tm.tm_mon == Mar)
{
// tm_year from 1900 count number
md.md_lastday_of_lastmon = isleap(tm.tm_year + 1900) ? LEAP_MONTH : NLEA_MONTH;
}
// If last month was also a solar month
switch (tm.tm_mon)
{
case Jan:
case Aug:
md.md_lastday_of_lastmon = SOLAR_MONTH;
break;
}
break;
/* lunar month date processing */
case Apr:
case Jun:
case Sep:
case Nov:
// lastday is 30
// lastday of lastmonth is 31
md.md_lastday = LUNAR_MONTH;
md.md_lastday_of_lastmon = SOLAR_MONTH;
break;
default:
md.md_lastday_of_lastmon = SOLAR_MONTH;
md.md_lastday = isleap(tm.tm_year + 1900) ? LEAP_MONTH : NLEA_MONTH;
break;
}
return md;
}
static struct tm *tz_convert(struct tm *tp, struct tz tz,
struct md md, struct tm *tmbuf)
{
struct tm *timep = tp;
/* East Timezone */
if (tz.tz_hour >= 0)
{
// If in half time zone (example: NPT+5:45 )
if (timep->tm_min >= MAX_MIN)
{
timep->tm_min -= MAX_MIN;
timep->tm_hour += 1;
if (timep->tm_hour >= MAX_HOUR)
{
timep->tm_hour -= MAX_HOUR;
timep->tm_wday += 1;
timep->tm_mday += 1;
timep->tm_yday += 1;
// Sunday when (tm_wday) is greater than (MAX_WEE - 1)
if (timep->tm_wday > (MAX_WEE - 1))
{
//timep->tm_wday = 0; // I don't like this way of writing. I think using operations will make logic clearer
timep->tm_wday -= MAX_WEE;
}
if (timep->tm_mday > md.md_lastday)
{
timep->tm_mday -= md.md_lastday;
timep->tm_mon += 1;
if (timep->tm_mon > MAX_MON)
{
timep->tm_mon -= MAX_MON;
timep->tm_year += 1;
}
}
if (timep->tm_yday > max_yday(timep->tm_year + 1900))
{
timep->tm_yday -= max_yday(timep->tm_year + 1900);
}
}
}
// If it is not in the half-time zone (example: CST+8:00)
else if (timep->tm_hour >= MAX_HOUR)
{
timep->tm_hour -= MAX_HOUR;
timep->tm_wday += 1;
timep->tm_mday += 1;
timep->tm_yday += 1;
// Sunday when (tm_wday) is greater than (MAX_WEE - 1)
if (timep->tm_wday > (MAX_WEE - 1))
{
//timep->tm_wday = 0; // I don't like this way of writing. I think using operations will make logic clearer
timep->tm_wday -= MAX_WEE;
}
if (timep->tm_mday > md.md_lastday)
{
timep->tm_mday -= md.md_lastday;
timep->tm_mon += 1;
if (timep->tm_mon > MAX_MON)
{
timep->tm_mon -= MAX_MON;
timep->tm_year += 1;
}
}
if (timep->tm_yday > max_yday(timep->tm_year + 1900))
{
timep->tm_yday -= max_yday(timep->tm_year + 1900);
}
}
}
/* West Timezone */
else if (tz.tz_hour < 0)
{
// If in half time zone (example: NST-3:30 )
if (timep->tm_min < MAX_MIN)
{
timep->tm_min += MAX_MIN;
timep->tm_hour -= 1;
if (timep->tm_hour < MAX_HOUR)
{
timep->tm_hour += MAX_HOUR;
timep->tm_wday -= 1;
timep->tm_mday -= 1;
timep->tm_yday -= 1;
if (timep->tm_wday < 0)
{
timep->tm_wday = (MAX_WEE - 1);
}
if (timep->tm_mday < md.md_lastday)
{
timep->tm_mday = md.md_lastday;
timep->tm_mon -= 1;
if (timep->tm_mon < MAX_MON)
{
timep->tm_mon = MAX_MON;
timep->tm_year -= 1;
}
}
if (timep->tm_yday < 0)
{
timep->tm_yday = max_yday(timep->tm_year + 1900);
}
}
}
// If it is not in the half-time zone (example: VET-4:00)
else if (timep->tm_hour < MAX_HOUR)
{
timep->tm_hour += MAX_HOUR;
timep->tm_wday -= 1;
timep->tm_mday -= 1;
timep->tm_yday -= 1;
if (timep->tm_wday < 0)
{
timep->tm_wday = (MAX_WEE - 1);
}
if (timep->tm_mday < md.md_lastday)
{
timep->tm_mday = md.md_lastday;
timep->tm_mon -= 1;
if (timep->tm_mon < MAX_MON)
{
timep->tm_mon = MAX_MON;
timep->tm_year -= 1;
}
}
if (timep->tm_yday < 0)
{
timep->tm_yday = max_yday(timep->tm_year + 1900);
}
}
}
/*- time pointer return -*/
if (tmbuf)
tmbuf = timep;
return timep;
}
struct tm* utc_to_local(struct tm* tp, struct tz tz)
{
static struct tm tm = { 0 }; // variable returned after
struct md md = { 0 };
// Add and subtract the original date into half-processed date
tm = tm_convert(tp, tz);
// Total number of days in this month and last month
md = get_monthday(tm);
/* timezone to convert */
return tz_convert(&tm, tz, md, &_tmbuf);
}
mktime_Firday.c
#include "libtm.h"
#ifdef __linux__
typedef long int long_int;
#else
typedef long long int long_int;
#endif
// unix time epoch year
#define EPOCH_YEAR 1970
// total number of leap year days in no leap year
#define DAY_LEAP 366
#define DAY_NLEAP 365
// number of seconds per day
#define DAY_SECS 86400
#ifdef month
enum month {
Jan, Feb, Mar, Apr, May, Jun,
Jul, Aug, Sep, Oct, Nov, Dec
};
#endif
// judge leap year
static int
leapyear(long_int year)
{
return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? 1 : 0;
}
// get the total number of days per month
static int
monthday(long_int year, long_int month)
{
switch (month)
{
case Jan:
case Mar:
case May:
case Jul:
case Aug:
case Oct:
case Dec:
return 31;
break;
case Apr:
case Jun:
case Sep:
case Nov:
return 30;
break;
case Feb:
return leapyear(year) ? 29 : 28;
break;
default:
return -1;
break;
}
}
time_t mktime_Ymdhms(int year, int mon, int mday,
int hour, int min, int sec)
{
static time_t t = 0L;
long_int leap = 0, nleap = 0;
/*
* from 00:00:00 on Jannuary 1, 1970, get a timestamps for a certain year
* note: (world standard time, remember!)
*/
for (time_t y = EPOCH_YEAR; y < year; y++)
{
// total from 1970 leap year and no leap year, number
leapyear(y) ? leap++ : nleap++;
}
t = ((DAY_LEAP * DAY_SECS) * leap) +
((DAY_NLEAP * DAY_SECS) * nleap);
/*
* the timestamps of the total number of days from January to a certain month in the year
* note: (because February has different days)
*/
for (long_int m = 0; m < mon - 1; m++)
{
t += monthday(year, m);
}
t += mday * DAY_SECS;
t += (hour * 60 * 60);
t += (min * 60);
t += sec;
/*
* the timestamp obtained is the first day of a month or a year.
* the extra day must be subtracted
*/
t -= DAY_SECS;
return t;
}
- 实现的后的效果