文档章节

日期转换星期函数

yonj1e
 yonj1e
发布于 2017/03/28 15:40
字数 1417
阅读 120
收藏 0

题目

写个日期函数

要求

  1. 输入日期(输入类型可为 date,timestamp 和 timestamptz),结果返回星期几。

先了解一下这几种日期类型

postgres=# create table a (r_date date,r_timestamp timestamp,r_timestamptz timestamptz);
CREATE TABLE
postgres=# \d a
					Table "public.a"
	Column     |            Type             | Modifiers 
---------------+-----------------------------+-----------
 r_date        | date                        | 
 r_timestamp   | timestamp without time zone | 
 r_timestamptz | timestamp with time zone    | 

postgres=# insert into a values (now(),now(),now());
INSERT 0 1
postgres=# select * from a;
   r_date   |        r_timestamp         |         r_timestamptz         
------------+----------------------------+-------------------------------
 2017-03-26 | 2017-03-26 19:30:19.816182 | 2017-03-26 19:30:19.816182-07	//日期 精度 时区
(1 row)

 接下来开始添加自定义日期函数

步骤可参考:https://my.oschina.net/u/3389270/blog/869121

先看一下这几个日期类型的 OID

postgres=# select oid,typname from pg_type where typname = 'timestamp' or typname = 'timestamptz' or typname = 'date';
 oid  |   typname   
------+-------------
 1082 | date
 1114 | timestamp
 1184 | timestamptz
(3 rows)

思路

首先添加函数定义,再添加实现函数,函数可正常获取参数及返回,添加日期转换星期几的功能

 

在 pg_proc.h 中添加函数定义

src/include/catalog/pg_proc.h

DATA(insert OID = 6664 (  date_to_week 	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1082 "1082" _null_ _null_ _null_ _null_ _null_ date_to_week _null_ _null_ _null_ ));
DESCR("date_to_week.");
DATA(insert OID = 6665 (  date_to_week 	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1114 "1114" _null_ _null_ _null_ _null_ _null_ timestamp_to_week _null_ _null_ _null_ ));
DESCR("timestamp_to_week.");
DATA(insert OID = 6666 (  date_to_week 	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 1184 "1184" _null_ _null_ _null_ _null_ _null_ timestamptz_to_week _null_ _null_ _null_ ));
DESCR("timestamptz_to_week.");

在 myfuncs.c 中添加实现函数

Datum date_to_week (PG_FUNCTION_ARGS)
{
	DateADT dateval = PG_GETARG_DATEADT(0);

	PG_RETURN_DATEADT(dateval);
}

Datum timestamp_to_week (PG_FUNCTION_ARGS)
{
	Timestamp timestampval = PG_GETARG_TIMESTAMP(0);

	PG_RETURN_TIMESTAMP(timestampval);
}

Datum timestamptz_to_week (PG_FUNCTION_ARGS)
{
	TimestampTz timestamptzval = PG_GETARG_TIMESTAMPTZ(0);

	PG_RETURN_TIMESTAMPTZ(timestamptzval);
}

以上函数只是获取日期,返回日期,函数可正常使用后可添加日期转星期的功能。

修改函数

src/backend/utils/adt/datetime.c

提供了 date 转换星期几的函数

/*
 * j2day - convert Julian date to day-of-week (0..6 == Sun..Sat)
 *
 * Note: various places use the locution j2day(date - 1) to produce a
 * result according to the convention 0..6 = Mon..Sun.  This is a bit of
 * a crock, but will work as long as the computation here is just a modulo.
 */
int
j2day(int date)
{
	unsigned int day;

	day = date;

	day += 1;
	day %= 7;

	return (int) day;
}	/* j2day() */

注意:

  1. 0..6 == Sun..Sat
  2. places use the locution j2day(date - 1) to produce a result

修改 pg_proc.h 函数定义的返回值类型 OID

DATA(insert OID = 6664 (  date_to_week 	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "1082" _null_ _null_ _null_ _null_ _null_ date_to_week _null_ _null_ _null_ ));
DESCR("date_to_week.");

修改 date_to_week() 函数,实现日期转换星期几

Datum date_to_week (PG_FUNCTION_ARGS)
{
	DateADT dateval = PG_GETARG_DATEADT(0);
	int week = j2day(dateval - 1);

	PG_RETURN_INT32(week);
}

 date 类型转换星期几已经完成,接下来的思路 timestamp 和 timestamptz 类型转换 date 类型,在重复以上即可。

src/backend/utils/adt/date.c
提供了 timestamp_date() 和 timestamptz_date() 函数:

/* timestamp_date()
 * Convert timestamp to date data type.
 */
Datum
timestamp_date(PG_FUNCTION_ARGS)
{
	Timestamp	timestamp = PG_GETARG_TIMESTAMP(0);
	DateADT		result;
	struct pg_tm tt,
			   *tm = &tt;
	fsec_t		fsec;

	if (TIMESTAMP_IS_NOBEGIN(timestamp))
		DATE_NOBEGIN(result);
	else if (TIMESTAMP_IS_NOEND(timestamp))
		DATE_NOEND(result);
	else
	{
		if (timestamp2tm(timestamp, NULL, tm, &fsec, NULL, NULL) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

		result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
	}

	PG_RETURN_DATEADT(result);
}

/* timestamptz_date()
 * Convert timestamp with time zone to date data type.
 */
Datum
timestamptz_date(PG_FUNCTION_ARGS)
{
	TimestampTz timestamp = PG_GETARG_TIMESTAMP(0);
	DateADT		result;
	struct pg_tm tt,
			   *tm = &tt;
	fsec_t		fsec;
	int			tz;

	if (TIMESTAMP_IS_NOBEGIN(timestamp))
		DATE_NOBEGIN(result);
	else if (TIMESTAMP_IS_NOEND(timestamp))
		DATE_NOEND(result);
	else
	{
		if (timestamp2tm(timestamp, &tz, tm, &fsec, NULL, NULL) != 0)
			ereport(ERROR,
					(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
					 errmsg("timestamp out of range")));

		result = date2j(tm->tm_year, tm->tm_mon, tm->tm_mday) - POSTGRES_EPOCH_JDATE;
	}

	PG_RETURN_DATEADT(result);
}

现在有了 timestamp_date() 和 timestamptz_date() 函数,接下来只需要调用完成类型转换就可以了。

src/include/fmgr.h

定义了调用单行函数的方法

/* These macros allow the collation argument to be omitted (with a default of
 * InvalidOid, ie, no collation).  They exist mostly for backwards
 * compatibility of source code.
 */
#define DirectFunctionCall1(func, arg1) \
	DirectFunctionCall1Coll(func, InvalidOid, arg1)
#define DirectFunctionCall2(func, arg1, arg2) \
	DirectFunctionCall2Coll(func, InvalidOid, arg1, arg2)
#define DirectFunctionCall3(func, arg1, arg2, arg3) \
	DirectFunctionCall3Coll(func, InvalidOid, arg1, arg2, arg3)
#define DirectFunctionCall4(func, arg1, arg2, arg3, arg4) \
	DirectFunctionCall4Coll(func, InvalidOid, arg1, arg2, arg3, arg4)
#define DirectFunctionCall5(func, arg1, arg2, arg3, arg4, arg5) \
	DirectFunctionCall5Coll(func, InvalidOid, arg1, arg2, arg3, arg4, arg5)
#define DirectFunctionCall6(func, arg1, arg2, arg3, arg4, arg5, arg6) \
	DirectFunctionCall6Coll(func, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6)
#define DirectFunctionCall7(func, arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
	DirectFunctionCall7Coll(func, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
#define DirectFunctionCall8(func, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
	DirectFunctionCall8Coll(func, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
#define DirectFunctionCall9(func, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) \
	DirectFunctionCall9Coll(func, InvalidOid, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9)

函数名后面的数字代表几个参数

src/backend/utils/fmgr/fmgr.c

/*
 * These are for invocation of a specifically named function with a
 * directly-computed parameter list.  Note that neither arguments nor result
 * are allowed to be NULL.  Also, the function cannot be one that needs to
 * look at FmgrInfo, since there won't be any.
 */
Datum
DirectFunctionCall1Coll(PGFunction func, Oid collation, Datum arg1)
{
	FunctionCallInfoData fcinfo;
	Datum		result;

	InitFunctionCallInfoData(fcinfo, NULL, 1, collation, NULL, NULL);

	fcinfo.arg[0] = arg1;
	fcinfo.argnull[0] = false;

	result = (*func) (&fcinfo);

	/* Check for null result, since caller is clearly not expecting one */
	if (fcinfo.isnull)
		elog(ERROR, "function %p returned NULL", (void *) func);

	return result;
}

DirectFunctionCall1Coll()  和 timestamp_date() 返回值类型都是 Datum

而我们的想法是 timestamp 类型的参数 经过 DirectFunctionCall1Coll() 调用 timestamp_date() 转换为 date 类型,但这里返回的是 Datum,所以还需要将 Datum 转换为 date

src/include/utils/date.h

定义了 Datum 类型转换为 date 类型的方法

#define DatumGetDateADT(X)	  ((DateADT) DatumGetInt32(X))

修改 timestamp_to_week() 函数:

Datum timestamp_to_week (PG_FUNCTION_ARGS)
{
	Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
	DateADT		result;
	result = DatumGetDateADT(DirectFunctionCall1(timestamp_date,timestamp));
	int week = j2day(result - 1);

	PG_RETURN_INT32(week);
}

同理 timestamptz 类型的也一样

完整的功能代码

/* pg_proc.h */

DATA(insert OID = 6664 (  date_to_week 	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "1082" _null_ _null_ _null_ _null_ _null_ date_to_week _null_ _null_ _null_ ));
DESCR("date_to_week.");
DATA(insert OID = 6665 (  date_to_week 	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "1114" _null_ _null_ _null_ _null_ _null_ timestamp_to_week _null_ _null_ _null_ ));
DESCR("timestamp_to_week.");
DATA(insert OID = 6666 (  date_to_week 	PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 23 "1184" _null_ _null_ _null_ _null_ _null_ timestamptz_to_week _null_ _null_ _null_ ));
DESCR("timestamptz_to_week.");
/* myfuns.c */

Datum date_to_week (PG_FUNCTION_ARGS)
{
	DateADT dateval = PG_GETARG_DATEADT(0);
	int week = j2day(dateval - 1);

	PG_RETURN_INT32(week);
}

Datum timestamp_to_week (PG_FUNCTION_ARGS)
{
	Timestamp timestamp = PG_GETARG_TIMESTAMP(0);
	DateADT		result;
	result = DatumGetDateADT(DirectFunctionCall1(timestamp_date,timestamp));
	int week = j2day(result - 1);

	PG_RETURN_INT32(week);
}

Datum timestamptz_to_week (PG_FUNCTION_ARGS)
{
	TimestampTz timestamptz = PG_GETARG_TIMESTAMPTZ(0);
	DateADT		result;
	result = DatumGetDateADT(DirectFunctionCall1(timestamptz_date,timestamptz));
	int week = j2day(result - 1);

	PG_RETURN_INT32(week);
}

结果

psql (9.5.6)
Type "help" for help.

postgres=# select date_to_week(timestamptz 'now()');
 date_to_week 
--------------
            2
(1 row)

postgres=# select date_to_week(timestamp 'now()');
 date_to_week 
--------------
            2
(1 row)

postgres=# select date_to_week(date 'now()');
 date_to_week 
--------------
            2
(1 row)

 

© 著作权归作者所有

上一篇: 时区转换函数
下一篇: 进制转换函数
yonj1e
粉丝 18
博文 20
码字总数 31358
作品 0
济南
后端工程师
私信 提问
MySQL 日期转换,时间转换,时间段查询

时区转换 转换数据库中已存时间的时区 获得国家地区时间格式 查询当前日期 时间 sysdate() 日期时间函数跟 now() 类似,不同之处在于:now() 在执行开始时值就得到了, sysdate() 在函数执行...

Grace_
05/05
101
0
excel设置日期字符为常规格式

excel中将日期设置为常规格式,比如"2017-05-03",可以先选其中一个进行设置 函数 =TEXT(A1,"0000-00-00"),然后就会生成常规格式的日期字符。 然后在复制这个值,在粘贴的时候,选择性粘贴为...

uknow8692
2017/05/03
43
0
MySQL的FROM_UNIXTIME()和UNIX_TIMESTAMP()函数的区别

Unix时间戳(Unix timestamp),是一种时间表示方式,定义为从格林威治时间1970年01月01日00时00分00秒起至现在的总秒数。 在MySQL中如何格式化时间戳? 在mysql中因为timestamp无法支持到毫秒...

度外网络
2012/09/26
12.3K
0
[Python] (Day-20) - 日期和时间

Only I can change my life. No one can do it for me. 只有我可以改变我的命运,没有人可以帮我做。 Python 提供了一个 time 和 calendar 模块可以用于格式化日期和时间 时间间隔是以秒为单...

Mazy
2017/11/03
0
0
实例介绍PHP日期函数date格式转换

网站开发时我们经常需要对日期、时间进行处理,在PHP里提供了很多日期、时间函数方便PHP开发者对日期、时间进行计算、格式转换。所以掌握PHP日期函数非常必要,也为处理PHP日期函数与Mysql数...

Adam-Lee
2011/08/29
2.5K
1

没有更多内容

加载失败,请刷新页面

加载更多

《JAVA核心知识》学习笔记 (21. JAVA 算法)

21. JAVA 算法

Shingfi
16分钟前
4
0
redis 命令

redis 秒杀用到的 原子锁 :$redis->decr('jili_reward_goods_stock_' . $gifts_id) redis 秒杀用到的原子锁在秒杀过程中库存量增加 $redis->incrBy('key1', 10); redis 键查看重复:$redis-......

小小小壮
16分钟前
4
0
像智能手机一样管理云端应用:阿里云联合微软全球首发开放应用模型(OAM)

2019 年 10 月 17 日上午 9 点 15 分,阿里巴巴合伙人、阿里云智能基础产品事业部总经理蒋江伟在 QCon 上海《基于云架构的研发模式演进》主题演讲中,正式宣布: “今天,我们同微软联合发布...

阿里巴巴云原生
25分钟前
4
0
SpringBoot配置数据源

默认数据源 Springboot默认支持4种数据源类型,定义在 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration 中,分别是: org.apache.tomcat.jdbc.pool.DataSource......

Gx_ww
28分钟前
4
0
Java应用在docker环境配置容器健康检查

在《极速体验docker容器健康》一文已体验了docker容器健康检查功能,今天就来给java应用的容器加入健康检查,使应用的状态随时都可以被监控和查看。 实战环境信息 操作系统:macOS Catalina ...

程序员欣宸
30分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部