C语言程序对夏令时的处理

背景

在设备应用中,用户需要根据自己的时区设置设备时区。在可交互的系统中或者完整的linux系统中可以通过设置/etc/TZ等时区文件链接的文件来修改设备时区。但是在一些rtos或者不能使用上面设置TZ文件方式的系统中,可以通过设置应用进程的TZ环境变量的方式来设置时区,此时在通过localtime等函数转换的时间就是本地时间了。

效果展示

$ make test 
./timezone
Europe/London        	 ts 1647169200, date 2022-03-13 11:00:00
America/Anchorage    ts 1647169199, date 2022-03-13 01:59:59
America/Anchorage    ts 1647169200, date 2022-03-13 03:00:00
Australia/Adelaide  	 ts 1647169200, date 2022-03-13 21:30:00


Europe/London        	 ts 1648917000, date 2022-04-02 17:30:00
America/Anchorage    ts 1648917000, date 2022-04-02 08:30:00
Australia/Adelaide   	ts 1648916999, date 2022-04-03 02:59:59
Australia/Adelaide   	ts 1648917000, date 2022-04-03 02:00:00

代码展示

citylist.h

#ifndef __CITYLIST_H_
#define __CITYLIST_H_

static const char cityList[][2][128]={
	{ "Pacific/Midway", "SST11" },
	{ "Pacific/Honolulu", "HST10" },
	{ "America/Anchorage", "AKST9AKDT,M3.2.0,M11.1.0" },
	{ "America/Los_Angeles", "PST8PDT,M3.2.0,M11.1.0" },
	{ "America/Tijuana", "PST8PDT,M3.2.0,M11.1.0" },
	{ "America/Phoenix", "MST7" },
	{ "America/Chihuahua", "MST7MDT,M4.1.0,M10.5.0" },
	{ "America/Denver", "MST7MDT,M3.2.0,M11.1.0" },
	{ "America/Costa_Rica", "CST6" },
	{ "America/Chicago", "CST6CDT,M3.2.0,M11.1.0" },
	{ "America/Mexico_City", "CST6CDT,M4.1.0,M10.5.0" },
	{ "America/Regina", "CST6" },
	{ "America/Bogota", "<-05>5" },
	{ "America/New_York", "EST5EDT,M3.2.0,M11.1.0" },
	{ "America/Caracas", "<-04>4" },
	{ "America/Barbados", "AST4" },
	{ "America/Halifax", "AST4ADT,M3.2.0,M11.1.0" },
	{ "America/Manaus", "<-04>4" },
	{ "America/Santiago", "" },
	{ "America/St_Johns", "NST3:30NDT,M3.2.0,M11.1.0" },
	{ "America/Sao_Paulo", "<-03>3" },
	{ "America/Argentina/Buenos_Aires", "<-03>3" },
	{ "America/Godthab", "" },
	{ "America/Montevideo", "<-03>3" },
	{ "Atlantic/South_Georgia", "<-02>2" },
	{ "Atlantic/Azores", "<-01>1<+00>,M3.5.0/0,M10.5.0/1" },
	{ "Atlantic/Cape_Verde", "<-01>1" },
	{ "Africa/Casablanca", "<+01>-1" },
	{ "Europe/London", "GMT0BST,M3.5.0/1,M10.5.0" },
	{ "Europe/Amsterdam", "CET-1CEST,M3.5.0,M10.5.0/3" },
	{ "Europe/Belgrade", "CET-1CEST,M3.5.0,M10.5.0/3" },
	{ "Europe/Brussels", "CET-1CEST,M3.5.0,M10.5.0/3" },
	{ "Europe/Sarajevo", "CET-1CEST,M3.5.0,M10.5.0/3" },
	{ "Africa/Windhoek", "CAT-2" },
	{ "Africa/Brazzaville", "WAT-1" },
	{ "Asia/Amman", "EET-2EEST,M3.5.4/24,M10.5.5/1" },
	{ "Europe/Athens", "EET-2EEST,M3.5.0/3,M10.5.0/4" },
	{ "Europe/Istanbul", "<+03>-3" },
	{ "Asia/Beirut", "EET-2EEST,M3.5.0/0,M10.5.0/0" },
	{ "Africa/Cairo", "EET-2" },
	{ "Europe/Helsinki", "EET-2EEST,M3.5.0/3,M10.5.0/4" },
	{ "Asia/Jerusalem", "" },
	{ "Europe/Minsk", "<+03>-3" },
	{ "Africa/Harare", "CAT-2" },
	{ "Asia/Baghdad", "<+03>-3" },
	{ "Europe/Moscow", "MSK-3" },
	{ "Asia/Kuwait", "<+03>-3" },
	{ "Africa/Nairobi", "EAT-3" },
	{ "Asia/Tehran", "<+0330>-3:30<+0430>,J79/24,J263/24" },
	{ "Asia/Baku", "<+04>-4" },
	{ "Asia/Tbilisi", "<+04>-4" },
	{ "Asia/Yerevan", "<+04>-4" },
	{ "Asia/Dubai", "<+04>-4" },
	{ "Asia/Kabul", "<+0430>-4:30" },
	{ "Asia/Karachi", "PKT-5" },
	{ "Asia/Oral", "<+05>-5" },
	{ "Asia/Yekaterinburg", "<+05>-5" },
	{ "Asia/Calcutta", "IST-5:30" },
	{ "Asia/Colombo", "<+0530>-5:30" },
	{ "Asia/Katmandu", "<+0545>-5:45" },
	{ "Asia/Almaty", "<+06>-6" },
	{ "Asia/Rangoon", "<+0630>-6:30" },
	{ "Asia/Krasnoyarsk", "<+07>-7" },
	{ "Asia/Bangkok", "<+07>-7" },
	{ "Asia/Jakarta", "WIB-7" },
	{ "Asia/Shanghai", "CST-8" },
	{ "Asia/Hong_Kong", "HKT-8" },
	{ "Asia/Irkutsk", "<+08>-8" },
	{ "Asia/Kuala_Lumpur", "<+08>-8" },
	{ "Australia/Perth", "AWST-8" },
	{ "Asia/Taipei", "CST-8" },
	{ "Asia/Seoul", "KST-9" },
	{ "Asia/Tokyo", "JST-9" },
	{ "Asia/Yakutsk", "<+09>-9" },
	{ "Australia/Adelaide", "ACST-9:30ACDT,M10.1.0,M4.1.0/3" },
	{ "Australia/Darwin", "ACST-9:30" },
	{ "Australia/Brisbane", "AEST-10" },
	{ "Australia/Hobart", "AEST-10AEDT,M10.1.0,M4.1.0/3" },
	{ "Australia/Sydney", "AEST-10AEDT,M10.1.0,M4.1.0/3" },
	{ "Asia/Vladivostok", "<+10>-10" },
	{ "Pacific/Guam", "ChST-10" },
	{ "Asia/Magadan", "<+11>-11" },
	{ "Pacific/Majuro", "<+12>-12" },
	{ "Pacific/Auckland", "NZST-12NZDT,M9.5.0,M4.1.0/3" },
	{ "Pacific/Fiji", "" },
	{ "Pacific/Tongatapu", "<+13>-13" }
};

#endif

main.c

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>

#include "citylist.h"

/**
 * @brief 根据设置的地区设置时区
 * */
int setTimeZone( const char *pRegion )
{
  int ret = 0;

  for( int i=0; i<sizeof(cityList)/2/128; i++ ){
    /* printf("--->%s,%d,%s\n", pRegion, i, cityList[i][0]); */
    if( strcmp( pRegion, cityList[i][0] ) == 0 ){
      setenv( "TZ", cityList[i][1], 1 );
      break;
    }
  }
  tzset();

  return ret;
}

void printTime(  const char *pRegion, time_t ts )
{
  setTimeZone(pRegion);

  struct tm *date = localtime( &ts );
  printf("%-20s ts %lu, date %4d-%02d-%02d %02d:%02d:%02d\n", pRegion, ts, date->tm_year+1900, date->tm_mon+1, date->tm_mday, date->tm_hour, date->tm_min, date->tm_sec);
}

int main()
{
  time_t ts;

  ts = 1647169200;

  printTime( "Europe/London", ts );
  printTime( "America/Anchorage", ts-1 );
  printTime( "America/Anchorage", ts );
  printTime( "Australia/Adelaide", ts );
  printf("\n\n");

  ts = 1648917000;

  printTime( "Europe/London", ts );
  printTime( "America/Anchorage", ts );
  printTime( "Australia/Adelaide", ts-1 );
  printTime( "Australia/Adelaide", ts );
  return 0;
}

Makefile

taget=timezone

all: $(taget)

$(taget): main.c
	gcc -o $@ $^

test: $(taget)
	./$(taget)

clean:
	rm $(taget)

拓展

其中citylist.h中的城市列表中的环境变量是从linux的时区文件中提取的。
提取环境变量的代码为:
tzif-display.c

#define _GNU_SOURCE
#define _POSIX_C_SOURCE
#include <stdio.h>		/// For printf, fopen, fclose, FILE
#include <stdlib.h>		/// For malloc, free
#include <error.h>		/// For error
#include <errno.h>		/// For errno
#include <string.h>		/// for memset, memcpy, memmem
#include <sys/stat.h>	/// for stat
#include <locale.h>		/// for setlocale
#include <time.h>       /// for ctime_r, tzset

#define TRUE -1
#define FALSE 0

/***************************************************
* definition of a tzfile header
***************************************************/
#define HEADER_LEN 44
typedef struct {
	char magicnumber[6];	  // = TZif2 or TZif\0
//	char reserved[15];		  // nulls
	long unsigned ttisgmtcnt; // number of UTC/local indicators stored in the file.
	long unsigned ttisstdcnt; // number of standard/wall indicators stored in the file.
	long unsigned leapcnt;    // number of leap seconds for which data is stored in the file.
	long unsigned timecnt;    // number of "transition times" for which data is stored in the file.
	long unsigned typecnt;    // number of "local time types" for which data is stored in the file (must not be zero).
	long unsigned charcnt;    // number of  characters  of  "timezone  abbreviation  strings" stored in the file.
	} timezonefileheader;
#define TZIF1_FIELD_SIZE 4
#define TZIF2_FIELD_SIZE 8


/***************************************************
* parse a tzfile "long format" value
***************************************************/
long parse_tz_long( const char *sourceptr, const int field_size)
{
	long retval = 0;
	char *long_ptr;

//	if (sizeof(long) < field_size)
//		printf("warning: field truncated because it is larger than a 'long' integer\n\n");

	int i,j;
	long_ptr = (char*) &retval;
	if ((field_size < sizeof(long)) && (sourceptr[0] >> 8))
	{
		for (i=sizeof(long)-1; (i>=field_size); i--) long_ptr[i] = 255; 
	}
	j = 0;
	for (i=field_size-1; (i>=0) && (j<sizeof(long)); i--)
	{
		long_ptr[j] = sourceptr[i];
		j++;
	}
	return retval;
}

/***************************************************
* read a tzheader file
***************************************************/
int read_tz_header( timezonefileheader *header,  const char *temp_buffer)
{
	const int field_size = 4;

	memcpy( header->magicnumber, &temp_buffer[0], 5 );
	header->magicnumber[5] = '\0';

	header->ttisgmtcnt = parse_tz_long(&temp_buffer[20], field_size);
	header->ttisstdcnt = parse_tz_long(&temp_buffer[24], field_size);
	header->leapcnt = parse_tz_long(&temp_buffer[28], field_size);
	header->timecnt = parse_tz_long(&temp_buffer[32], field_size);
	header->typecnt = parse_tz_long(&temp_buffer[36], field_size);
	header->charcnt = parse_tz_long(&temp_buffer[40], field_size);

	if (header->typecnt == 0)
	{
		printf("Error in file format. Zero local time types suggested.\n");
		return FALSE;
	}

	if (header->timecnt == 0)
	{
		printf("No transition times recorded in this file\n");
		return FALSE;
	}

return TRUE;
}

/***********************************************************************
* tzif2_handle
***********************************************************************/
char* tzif2_handle( timezonefileheader *tzh, const char *tzfile_buffer_ptr, size_t buffer_size )
{
	char *start_ptr;
	char magicnumber[5] = "TZif2";

	start_ptr = memmem( tzfile_buffer_ptr, buffer_size, &magicnumber, 5 );
	if (start_ptr == NULL)
	{
		printf("error finding tzif2 header\n");
		return NULL;
	}

	if ( read_tz_header( tzh, (char*) start_ptr ) == FALSE )
	{
		printf("Error reading header file version 2\n");
		return NULL;
	}

	return start_ptr + HEADER_LEN;
}


/***************************************************
* Display contents of a timezone file
***************************************************/
void handle_tzfile_data(	char *tz,
                            const timezonefileheader tzh,
							const char* tzfile_buffer_ptr,
							const int field_size /// 4bytes for tzif1, 8bytes for tzif2
							)
{

	 /***************************************************
	 * 
	 * Structure of the timezone files
	 * 
	 ***************************************************/
		char *local_time_type_ptr;

		char *ttinfo_read_ptr;
		char* tz_abbrev_ptr;
		char *leapinfo_ptr;
		char *wall_indicator_ptr;
		char *local_indicator_ptr;


 /********************************************************************
 * 
 * Beginning of code for function display_tzfile_data
 * 
 ********************************************************************/

	local_time_type_ptr = (char*) tzfile_buffer_ptr + tzh.timecnt*field_size;
	ttinfo_read_ptr = local_time_type_ptr + tzh.timecnt;

	tz_abbrev_ptr = ttinfo_read_ptr + (tzh.typecnt * 6);

	leapinfo_ptr = tz_abbrev_ptr + (tzh.charcnt);


	wall_indicator_ptr = leapinfo_ptr + (tzh.leapcnt*field_size*2);


	local_indicator_ptr = wall_indicator_ptr + tzh.ttisstdcnt;

	if (field_size == TZIF2_FIELD_SIZE)
	{
		char *start_line_ptr;
		start_line_ptr = strchr( local_indicator_ptr + tzh.ttisgmtcnt, 10 );
		if (start_line_ptr == NULL)
			printf("\nno \'general rule\' information found at end of file\n");
		else
		{
			char *end_line_ptr;
			end_line_ptr = strchr( start_line_ptr+1, 10 );
			if (end_line_ptr == NULL)
				printf("\nerror finding \'general rule\' info terminator symbol\n");
			else
			{
				*end_line_ptr = '\0';
				/* printf("\ngeneral rule (unparsed): %s\n", start_line_ptr+1 ); */
                memcpy( tz, start_line_ptr+1, end_line_ptr - start_line_ptr );
			}
			
		}
			
	}

}

#define TZ_DIR_LIST "/usr/share/zoneinfo"
int GetTZValFromZoneinfo(const char *zoneInfo, char *TZ )
{
    int ret = 0;
	FILE *tz_file;
	char *tzif_buffer_ptr;
	char *start_ptr;
	int   field_size;
	struct stat file_status;
	timezonefileheader tzh;

    do{
        char zoneInfoPath[128];
        snprintf( zoneInfoPath, sizeof(zoneInfoPath),"%s/%s", TZ_DIR_LIST, zoneInfo );

        tz_file = fopen( zoneInfoPath, "rb");

        if (tz_file == NULL)
        {
            error(errno,0,"tz file %s not found\n", zoneInfoPath);
            ret = -1;
            break;
        }

        if (fstat( fileno( tz_file), &file_status) != 0)
        {
            printf("error retreiving file status\n");
            ret = -1;
            break;
        }

        tzif_buffer_ptr = (char *) malloc( file_status.st_size );
        if (tzif_buffer_ptr == NULL)
        {
            printf("memory allocation error - tzif buffer\n");
            ret = errno;
            break;
        }

        if ( fread( tzif_buffer_ptr, file_status.st_size, 1, tz_file) != 1 )
        {
            printf("error reading into tzif buffer\n");
            free(tzif_buffer_ptr);
            ret = errno;
            break;
        }
        fclose(tz_file);


        if ( read_tz_header( &tzh, tzif_buffer_ptr ) == FALSE )
        {
            printf("Error reading header file version 1\n");
            ret = -1;
            break;
        }

        if (tzh.magicnumber[4] == 50 )
        {
            start_ptr = tzif2_handle( &tzh, &tzif_buffer_ptr[HEADER_LEN], file_status.st_size - HEADER_LEN);
            field_size = TZIF2_FIELD_SIZE;
        }
        else
        {
            start_ptr = &tzif_buffer_ptr[HEADER_LEN];
            field_size = TZIF1_FIELD_SIZE;
        }
        if (start_ptr != NULL ) handle_tzfile_data( TZ, tzh, start_ptr, field_size );

        free(tzif_buffer_ptr);
    }while(0);


    return ret;
}

void main( int argc, const char *argv[] )
{
    char tz[128];
    /* GetTZValFromZoneinfo( "America/New_York", tz ); */
    memset( tz, 0, sizeof(tz) );
    GetTZValFromZoneinfo( argv[1], tz );
    printf("tz = %s\n", tz );
}

编译执行:

$ gcc tzif-display.c
$ ./a.out Cuba
tz = CST5CDT,M3.2.0/0,M11.1.0/1

如上就可以提取Cuba的环境变量为CST5CDT,M3.2.0/0,M11.1.0/1,根据需要提取对应的城市地区的环境变量

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值