前言:
pg_cron插件默认时区为GMT,对于其他时区的用户来说使用时要计算时区差异,比较繁琐。这篇博客通过修改pg_cron1.3的源码,来实现一种简单的更改时区方法。未经过详细测试,本人水平有限,可能会导致pg_cron出现问题,在此仅记录修改思路。
pg_cron源码改动
pg_cron中对于cron格式的解析在parse_cron_entry()函数中,该函数由vixie-cron的load_entry修改而来,事实上,pg_cron中对于cron的解析和执行判断很多逻辑都来自于vixie-cron,对于其具体逻辑的分析,本博客不再赘述。
在pg_cron中,对于job是否需要执行的判断在ShouldRunTask()函数中,源代码如下
static bool
ShouldRunTask(entry *schedule, TimestampTz currentTime, bool doWild,
bool doNonWild)
{
time_t currentTime_t = timestamptz_to_time_t(currentTime);
struct tm *tm = gmtime(¤tTime_t);
int minute = tm->tm_min -FIRST_MINUTE;
int hour = tm->tm_hour -FIRST_HOUR;
int dayOfMonth = tm->tm_mday -FIRST_DOM;
int month = tm->tm_mon +1 -FIRST_MONTH;
int dayOfWeek = tm->tm_wday -FIRST_DOW;
if (bit_test(schedule->minute, minute) &&
bit_test(schedule->hour, hour) &&
bit_test(schedule->month, month) &&
( ((schedule->flags & DOM_STAR) || (schedule->flags & DOW_STAR))
? (bit_test(schedule->dow,dayOfWeek) && bit_test(schedule->dom,dayOfMonth))
: (bit_test(schedule->dow,dayOfWeek) || bit_test(schedule->dom,dayOfMonth)))) {
if ((doNonWild && !(schedule->flags & (MIN_STAR|HR_STAR)))
|| (doWild && (schedule->flags & (MIN_STAR|HR_STAR))))
{
return true;
}
}
return false;
}
关注点在于这两句代码
time_t currentTime_t = timestamptz_to_time_t(currentTime);
struct tm *tm = gmtime(¤tTime_t);
这两句的作用是将我们获取的currentTime转换为time_t格式,再用gmtime()函数来填充tm结构并用GMT时区表示,结构体格式如下
/* ISO C `broken-down time' structure. */
struct tm
{
int tm_sec; /* Seconds. [0-60] (1 leap second) */
int tm_min; /* Minutes. [0-59] */
int tm_hour; /* Hours. [0-23] */
int tm_mday; /* Day. [1-31] */
int tm_mon; /* Month. [0-11] */
int tm_year; /* Year - 1900. */
int tm_wday; /* Day of week. [0-6] */
int tm_yday; /* Days in year.[0-365] */
int tm_isdst; /* DST. [-1/0/1]*/
# ifdef __USE_MISC
long int tm_gmtoff; /* Seconds east of UTC. */
const char *tm_zone; /* Timezone abbreviation. */
# else
long int __tm_gmtoff; /* Seconds east of UTC. */
const char *__tm_zone; /* Timezone abbreviation. */
# endif
};
因此,想要更改时区,就应当生成与数据库时区相同的tm结构体
gmtime()函数为c库函数,第一种方案我们可以使用同为c库函数的localtime(),但这个函数获取的是操作系统本地时区,返回的tm结构体以操作系统本地时区表示,但pg_cron是基于postgresql数据库的,数据库时区可能与操作系统本地时区不符。因此,我们使用postgresql的pg_localtime()函数。该函数的声明在pgtime.h中
extern struct pg_tm *pg_localtime(const pg_time_t *timep, const pg_tz *tz);
与localtime()函数相比,多了一个参数,该参数的作用是传入一个pg_tz结构体,用于自定义返回的pg_tm结构体的表现时区,具体使用代码如下。
pg_time_t currentTime_t = timestamptz_to_time_t(currentTime);
struct pg_tm *tm;
pg_tz *tz;
tz = pg_tzset("PRC");//东八区北京时间
if (!tz)
return false;//也可以改成ereport报告一下错误
tm = pg_localtime(¤tTime_t, tz);
也可以创建一个系统参数,比如cron.time_zone,调用pg_tzset()函数时传入该参数,这样可以实现多时区改变。