PostgreSQL系统表或视图中pg_node_tree类型值解析

news/2024/11/13 9:34:10/文章来源:https://www.cnblogs.com/knlbase/p/18538890

PostgreSQL系统表或视图中pg_node_tree类型值解析

pg_node_tree类型说明

pg_node_tree是一种openGauss/PostgreSQL内部数据类型,用于表示树形结构的数据。这个数据类型通常对用户不可见,因此直接查询或操作它的内容通常需要使用一些PostgreSQL的内置函数或工具。

包含pg_node_tree类型的系统视图/表,以pg12版本为例:

postgres=# select table_schema,table_name,column_name from information_schema.columns where data_type = 'pg_node_tree';table_schema |      table_name      |  column_name
--------------+----------------------+----------------pg_catalog   | pg_proc              | proargdefaultspg_catalog   | pg_type              | typdefaultbinpg_catalog   | pg_class             | relpartboundpg_catalog   | pg_attrdef           | adbinpg_catalog   | pg_constraint        | conbinpg_catalog   | pg_index             | indexprspg_catalog   | pg_index             | indpredpg_catalog   | pg_rewrite           | ev_qualpg_catalog   | pg_rewrite           | ev_actionpg_catalog   | pg_trigger           | tgqualpg_catalog   | pg_policy            | polqualpg_catalog   | pg_policy            | polwithcheckpg_catalog   | pg_partitioned_table | partexprs
(13 rows)postgres=#

解析对应关系

系统表/视图 字段名 解析使用表达式
pg_proc proargdefaults pg_get_expr(proargdefaults,'pg_proc'::regclass)
pg_get_function_arguments(oid)
pg_type typdefaultbin
pg_class relpartbound pg_get_expr(relpartbound,oid)
pg_attrdef adbin pg_get_expr(adbin,adrelid)
pg_constraint conbin pg_get_expr(conbin,conrelid)
pg_get_constraintdef(oid)
pg_index indexprs pg_get_expr(indexprs,indrelid)
pg_index indpred pg_get_expr(indpred,indrelid)
pg_rewrite ev_qual
pg_rewrite ev_action
pg_trigger tgqual pg_get_triggerdef(oid)
pg_policy polqual
pg_policy polwithcheck
pg_partitioned_table partexprs pg_get_expr(partexprs,partrelid)

pg_node_tree类型值解析

对于存储再pg_node_tree类型中的数据,可以使用pg_get_expr()函数将其转换为可读的SQL表达式。这个函数将抽象的树形结构转换为一个人类可读的SQL表达式形式。

pg_get_expr函数说明:

pg_get_expr(pg_node_tree, relation_oid)
pg_get_expr(pg_node_tree, relation_oid, pretty_bool)返回类型:text

反编译一个表达式的内部形式,假定其中的任何 Var 指向由第二个参数指示的关系

解析pg_attrdef.adbin

pg_attrdef存储列的默认值。列的主要信息存储在pg_attribute。只有那些显式指定了一个默认值的列才会在这个目录中有一个项。

-- 创建测试表
drop table if exists test_t;
create table test_t (id int,name varchar(20) default 'test',update_time timestamp default current_timestamp);-- 原查询结果
select t1.adrelid::regclass,t2.attname,adbin
from pg_attrdef t1
join pg_attribute t2 on t1.adrelid=t2.attrelid and t1.adnum=t2.attnum
where t2.attnum>0and t1.adrelid::regclass::text='test_t';
-[ RECORD 1 ]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
adrelid | test_t
attname | name
adbin   | {FUNCEXPR :funcid 669 :funcresulttype 1043 :funcretset false :funcvariadic false :funcformat 2 :funccollid 100 :inputcollid 100 :args ({CONST :consttype 1043 :consttypmod -1 :constcollid 100 :constlen -1 :constbyval false :constisnull false :location 53 :constvalue 8 [ 32 0 0 0 116 101 115 116 ]} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :location -1 :constvalue 4 [ 24 0 0 0 0 0 0 0 ]} {CONST :consttype 16 :consttypmod -1 :constcollid 0 :constlen 1 :constbyval true :constisnull false :location -1 :constvalue 1 [ 0 0 0 0 0 0 0 0 ]}) :location -1}
-[ RECORD 2 ]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
adrelid | test_t
attname | update_time
adbin   | {FUNCEXPR :funcid 2027 :funcresulttype 1114 :funcretset false :funcvariadic false :funcformat 2 :funccollid 0 :inputcollid 0 :args ({SQLVALUEFUNCTION :op 3 :type 1184 :typmod -1 :location 90}) :location -1}-- 解析后
select t1.adrelid::regclass,t2.attname,pg_get_expr(adbin,t1.adrelid)
from pg_attrdef t1
join pg_attribute t2 on t1.adrelid=t2.attrelid and t1.adnum=t2.attnum
where t2.attnum>0and t1.adrelid::regclass::text='test_t';adrelid |   attname   |        pg_get_expr
---------+-------------+---------------------------test_t  | name        | 'test'::character varyingtest_t  | update_time | CURRENT_TIMESTAMP
(2 rows)      

解析pg_proc.proargdefaults

-- 创建测试函数
CREATE OR REPLACE FUNCTION test_fun(arg1 INTEGER,arg2 INTEGER DEFAULT 0,arg3 TEXT DEFAULT 'default_value'
)
RETURNS INTEGER
AS $$
BEGINRETURN arg1 + arg2;
END;
$$ LANGUAGE plpgsql;-- 正常查询
postgres=# select proargdefaults from pg_proc where proname = 'test_fun';proargdefaults
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------({CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :location 80 :constvalue 4 [ 0 0 0 0 0 0 0 0 ]} {CONST :consttype 25 :consttypmod -1 :constcollid 100 :constlen -1 :constbyval false :constisnull false :location 105 :constvalue 17 [ 68 0 0 0 100 101 102 97 117 108 116 95 118 97 108 117 101 ]})
(1 row)postgres=#-- 解析后
postgres=# select pg_get_expr(proargdefaults,'pg_proc'::regclass) from pg_proc where proname = 'test_fun';pg_get_expr
--------------------------0, 'default_value'::text
(1 row)-- 或者
postgres=# select proname,pg_get_function_arguments(oid) from pg_proc where proname = 'test_fun';proname  |                           pg_get_function_arguments
----------+-------------------------------------------------------------------------------test_fun | arg1 integer, arg2 integer DEFAULT 0, arg3 text DEFAULT 'default_value'::text
(1 row)

解析pg_index.indexprs和pg_index.indpred

-- 创建测试表和索引
drop table if exists test_t;
create table test_t(id int,name varchar(100));
create index idx_name on test_t(lower(name)) where id >=100000;-- 查询
select t3.relname as tablename,t1.relname as indexname,t2.indexprs,t2.indpredfrom pg_class t1join pg_index t2 on t1.oid=t2.indexrelidjoin pg_class t3 on t3.oid=t2.indrelid
where t3.relname = 'test_t';-- 原值
tablename | test_t
indexname | idx_name
indexprs  | ({FUNCEXPR :funcid 870 :funcresulttype 25 :funcretset false :funcvariadic false :funcformat 0 :funccollid 100 :inputcollid 100 :args ({RELABELTYPE :arg {VAR :varno 1 :varattno 2 :vartype 1043 :vartypmod 104 :varcollid 100 :varlevelsup 0 :varnoold 1 :varoattno 2 :location 38} :resulttype 25 :resulttypmod -1 :resultcollid 100 :relabelformat 2 :location -1}) :location 32})
indpred   | {OPEXPR :opno 525 :opfuncid 150 :opresulttype 16 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 1 :location 51} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :location 56 :constvalue 4 [ -96 -122 1 0 0 0 00 ]}) :location 54}-- 解析后的值
select t3.relname as tablename,t1.relname as indexname,pg_get_expr(t2.indexprs,indrelid),pg_get_expr(t2.indpred,indrelid)from pg_class t1join pg_index t2 on t1.oid=t2.indexrelidjoin pg_class t3 on t3.oid=t2.indrelid
where t3.relname = 'test_t';tablename | indexname |     pg_get_expr     |  pg_get_expr
-----------+-----------+---------------------+----------------test_t    | idx_name  | lower((name)::text) | (id >= 100000)
(1 row)

解析pg_class.relpartbound

-- 创建分区表
CREATE TABLE test_range_t (id serial,date timestamp(6),data TEXT) PARTITION BY RANGE (date);
CREATE TABLE test_range_2020 PARTITION OF test_range_t FOR VALUES FROM ('2020-01-01') TO ('2021-01-01');
CREATE TABLE test_range_2021 PARTITION OF test_range_t FOR VALUES FROM ('2021-01-01') TO ('2022-01-01');
CREATE TABLE test_range_2022 PARTITION OF test_range_t FOR VALUES FROM ('2022-01-01') TO ('2023-01-01');
CREATE TABLE test_range_2023 PARTITION OF test_range_t FOR VALUES FROM ('2023-01-01') TO ('2024-01-01');-- 某一个分区的值查询
postgres=# select relname,relpartbound from pg_class where relname = 'test_range_2020';
-[ RECORD 1 ]+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
relname      | test_range_2020
relpartbound | {PARTITIONBOUNDSPEC :strategy r :is_default false :modulus 0 :remainder 0 :listdatums <> :lowerdatums ({PARTITIONRANGEDATUM :kind 0 :value {CONST :consttype 1114 :consttypmod 6 :constcollid 0 :constlen 8 :constbyval true :constisnull false :location -1 :constvalue 8 [ 0 96 -62 -122 7 62 2 0 ]} :location 72}) :upperdatums ({PARTITIONRANGEDATUM :kind 0 :value {CONST :consttype 1114 :consttypmod 6 :constcollid 0 :constlen 8 :constbyval true :constisnull false :location -1 :constvalue 8 [0 -96 -83 48 -54 90 2 0 ]} :location 90}) :location 66}-- 解析后
postgres=# select relname,pg_get_expr(relpartbound,oid) from pg_class where relname ~ 'test_range_\d+';relname     |                            pg_get_expr
-----------------+--------------------------------------------------------------------test_range_2020 | FOR VALUES FROM ('2020-01-01 00:00:00') TO ('2021-01-01 00:00:00')test_range_2021 | FOR VALUES FROM ('2021-01-01 00:00:00') TO ('2022-01-01 00:00:00')test_range_2022 | FOR VALUES FROM ('2022-01-01 00:00:00') TO ('2023-01-01 00:00:00')test_range_2023 | FOR VALUES FROM ('2023-01-01 00:00:00') TO ('2024-01-01 00:00:00')
(4 rows)

解析pg_constraint.conbin

drop table if exists test_t;
create table test_t (id int,name varchar(100),check(id>0),check(length(name)>3));-- 原值查询
select connamespace::regnamespace as schema,conrelid::regclass as tablename,contype,conname,conbin
from pg_constraint where conrelid::regclass::text='test_t';
-[ RECORD 1 ]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
schema    | public
tablename | test_t
contype   | c
conname   | test_t_id_check
conbin    | {OPEXPR :opno 521 :opfuncid 147 :opresulttype 16 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 1 :varattno 1 :vartype 23 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 1 :location 52} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :location 55 :constvalue 4 [ 0 0 0 0 0 0 0 0 ]}) :location 54}
-[ RECORD 2 ]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
schema    | public
tablename | test_t
contype   | c
conname   | test_t_name_check
conbin    | {OPEXPR :opno 521 :opfuncid 147 :opresulttype 16 :opretset false :opcollid 0 :inputcollid 0 :args ({FUNCEXPR :funcid 1317 :funcresulttype 23 :funcretset false :funcvariadic false :funcformat 0 :funccollid 0 :inputcollid 100 :args ({RELABELTYPE :arg {VAR :varno 1 :varattno 2 :vartype 1043 :vartypmod 104 :varcollid 100 :varlevelsup 0 :varnoold 1 :varoattno 2 :location 71} :resulttype 25 :resulttypmod -1 :resultcollid 100 :relabelformat 2 :location -1}) :location 64} {CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :location 77 :constvalue 4 [ 3 0 0 0 0 0 0 0 ]}) :location 76}-- 解析后
select connamespace::regnamespace as schema,conrelid::regclass as tablename,contype,conname,pg_get_expr(conbin,conrelid)
from pg_constraint where conrelid::regclass::text='test_t';schema | tablename | contype |      conname      |        pg_get_expr
--------+-----------+---------+-------------------+----------------------------public | test_t    | c       | test_t_id_check   | (id > 0)public | test_t    | c       | test_t_name_check | (length((name)::text) > 3)
(2 rows)-- 或者
select connamespace::regnamespace as schema,conrelid::regclass as tablename,contype,conname,pg_get_constraintdef(oid)
from pg_constraint where conrelid::regclass::text='test_t';schema | tablename | contype |      conname      |        pg_get_constraintdef
--------+-----------+---------+-------------------+------------------------------------public | test_t    | c       | test_t_id_check   | CHECK ((id > 0))public | test_t    | c       | test_t_name_check | CHECK ((length((name)::text) > 3))
(2 rows)

解析pg_partitioned_table.partexprs

CREATE TABLE sales (id SERIAL,sale_date DATE NOT NULL,amount NUMERIC NOT NULL,region TEXT NOT NULL
) PARTITION BY RANGE (EXTRACT(YEAR FROM sale_date));
CREATE TABLE sales_2022 PARTITION OF sales FOR VALUES FROM (2022) TO (2023);
CREATE TABLE sales_2023 PARTITION OF sales FOR VALUES FROM (2023) TO (2024);
CREATE TABLE sales_2024 PARTITION OF sales FOR VALUES FROM (2024) TO (2025);-- 原值
postgres=# select partrelid::regclass,partexprs from pg_partitioned_table where partrelid::regclass::text='sales';
-[ RECORD 1 ]------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
partrelid | sales
partexprs | ({FUNCEXPR :funcid 1384 :funcresulttype 701 :funcretset false :funcvariadic false :funcformat 0 :funccollid 0 :inputcollid 100 :args ({CONST :consttype 25 :consttypmod -1 :constcollid 100 :constlen -1 :constbyval false :constisnull false :location 149 :constvalue 8 [ 32 0 0 0 121 101 97 114 ]} {VAR :varno 1 :varattno 2 :vartype 1082 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 1 :varoattno 2 :location 159}) :location 141})-- 解析后
postgres=# select partrelid::regclass,pg_get_expr(partexprs,partrelid) from pg_partitioned_table where partrelid::regclass::text='sales';partrelid |            pg_get_expr
-----------+------------------------------------sales     | date_part('year'::text, sale_date)
(1 row)

 注意:date_part函数等价与extract 

postgres=# select extract(year from now()) as extract,date_part('year'::text,now()) as date_part;extract | date_part
---------+-----------2024 |      2024
(1 row)postgres=# select extract(year from current_date) as extract,date_part('year'::text,current_date) as date_part;extract | date_part
---------+-----------2024 |      2024
(1 row)

解析pg_trigger.tgqual

-- 创建order表
CREATE TABLE orders (id SERIAL PRIMARY KEY,order_date DATE NOT NULL,amount NUMERIC NOT NULL,customer_name VARCHAR(100)
);-- audit_log表
CREATE TABLE audit_log (id SERIAL PRIMARY KEY,order_id INT,action VARCHAR(50),log_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);-- 创建触发器函数
CREATE OR REPLACE FUNCTION log_high_value_order()
RETURNS TRIGGER AS $$
BEGIN-- 检查订单金额是否超过 1000IF NEW.amount > 1000 THEN-- 插入审计日志INSERT INTO audit_log (order_id, action) VALUES (NEW.id, 'High Value Order');END IF;RETURN NEW;
END;
$$ LANGUAGE plpgsql;-- 创建带有条件的触发器
CREATE TRIGGER high_value_order_trigger
AFTER INSERT ON orders
FOR EACH ROW
WHEN (NEW.amount > 1000)
EXECUTE FUNCTION log_high_value_order();

查询触发器信息

-- 原值
postgres=# select tgrelid::regclass,tgname,tgqual from pg_trigger where tgrelid::regclass::text='orders';
-[ RECORD 1 ]--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
tgrelid | orders
tgname  | high_value_order_trigger
tgqual  | {OPEXPR :opno 1756 :opfuncid 1720 :opresulttype 16 :opretset false :opcollid 0 :inputcollid 0 :args ({VAR :varno 2 :varattno 3 :vartype 1700 :vartypmod -1 :varcollid 0 :varlevelsup 0 :varnoold 2 :varoattno 3 :location 82} {FUNCEXPR :funcid 1740 :funcresulttype 1700 :funcretset false :funcvariadic false :funcformat 2 :funccollid 0 :inputcollid 0 :args ({CONST :consttype 23 :consttypmod -1 :constcollid 0 :constlen 4 :constbyval true :constisnull false :location 95 :constvalue 4 [ -24 3 0 0 0 0 0 0 ]}) :location -1}) :location 93}-- 解析后
postgres=# select tgrelid::regclass,tgname,pg_get_triggerdef(oid) as trigger_definition from pg_trigger where tgrelid::regclass::text='orders';
-[ RECORD 1 ]------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------
tgrelid            | orders
tgname             | high_value_order_trigger
trigger_definition | CREATE TRIGGER high_value_order_trigger AFTER INSERT ON public.orders FOR EACH ROW WHEN ((new.amount > (1000)::numeric)) EXECUTE FUNCTION log_high_value_order()

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

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

相关文章

2024.11.6(周三)

用透明组合模式实现教材中的“文件夹浏览”这个例子。 实验要求: 1.文件的执行不需真正实现,只需简单提示即可; 2.提交源代码; 3.注意编程规范。1、类图2、源代码 #include <iostream> #include <string> #include<list> using namespace std;class Abst…

2024.11.7(周四)

用装饰模式模拟手机功能的升级过程:简单的手机(SimplePhone)在接收来电时,会发出声音提醒主人;而JarPhone除了声音还能振动;更高级的手机(ComplexPhone)除了声音、振动外,还有灯光闪烁提示。 实验要求: 1.提交类图; 2.提交源代码; 3.注意编程规范。1、类图2、源代码 #i…

2024.11.5(周二)

用桥接模式实现在路上开车这个问题,其中,车可以是car或bus,路可以是水泥路或沥青路。 实验要求: 1.画出对应的类图; 2.提交源代码; 3.注意编程规范。1、类图2、源代码 (1) Bus.java package test;public class Bus implements Vehicle{@Overridepublic void run() {Sy…

CFAT:三角窗口实现图像超分辨率

CFAT:三角窗口实现图像超分辨率基于变换器的模型通过利用其固有的捕获复杂上下文特征的能力,彻底改变了图像超分辨率(SR)的效果。如今,在Transformer架构中使用的重叠矩形移位窗口技术是超分辨率模型中的一种常见做法,可以提高图像放大的质量和鲁棒性。然而,它在边界处存…

CFAT:释放三角窗口实现图像超分辨率

CFAT:释放三角窗口实现图像超分辨率基于变换器的模型通过利用其固有的捕获复杂上下文特征的能力,彻底改变了图像超分辨率(SR)的效果。如今,在Transformer架构中使用的重叠矩形移位窗口技术是超分辨率模型中的一种常见做法,可以提高图像放大的质量和鲁棒性。然而,它在边界…

读数据工程之道:设计和构建健壮的数据系统34读后总结与感想兼导读

读后总结与感想兼导读1. 基本信息 数据工程之道:设计和构建健壮的数据系统[美]乔里斯(Joe Reis),[美]马特豪斯利(Matt Housley)著机械工业出版社,2024年2月出版1.1. 读薄率 书籍总字数473千字,笔记总字数109584字。 读薄率109584473000≈23.17% 1.2. 读厚方向Data Mesh权威指…

鸿蒙NEXT开发案例:抛硬币

【1】引言(完整代码在最后面) 本项目旨在实现一个简单的“抛硬币”功能,用户可以通过点击屏幕上的地鼠图标来模拟抛硬币的过程。应用会记录并显示硬币正面(地鼠面)和反面(数字100面)出现的次数。为了增强用户体验,我们还添加了动画效果,使抛硬币的过程更加生动有趣。 …

【CodeForces训练记录】Codeforces Round 986 (Div. 2)

训练情况赛后反思 C题逆风翻盘,可能勉强青名了。A题愣神了,我觉得还能再做的快一点。 A题 给定一个字符串,NWSE,重复着字符串走,我们直接模拟即可,用 while 来判断是否走到终点,然后对于不可能走到的终点,我选择了一个不会超时的步数范围,超出就跳出 while 即可,最后…

24. 使用MySQL之使用游标

1. 游标 由前几章可知,MySQL检索操作返回一组称为结果集的行。这组返回的行都是与SQL语句相匹配的行(零行或多行)。 使用简单的SELECT语句,例如,没有办法得到第一行、下一行或前10行,也不存在每次一行地处理所有行的简单方法(相对于成批地处理它们)。 有时,需要在检索…

Python clickhouse-driver 类库使用学习总结

实践环境 python3 .9.13 clickhouse-driver 0.2.9 实践操作 # -*- coding:utf-8 -*-import clickhouse_driverif __name__ == __main__:host = 192.168.88.131port = 9000 # 注意,不能使用默认的8123username = testaccpassword = test1234database = default# 连接方式1# con…

随波逐流工具使用_Week1

跟着大师傅的公众号做题的week1 来源以及说明 (文章主要是了解怎样使用长弓三皮大师傅的随波逐流工具,wp以及附件来自大师傅长弓三皮) (这周主要是做笔记的软件老是出现问题,有一些笔记有点乱,后面慢慢改进) 软件及题目下载 http://www.1o1o.xyz/bo_softdown.html CTF题目wr…

开源 - Ideal库 - 特殊时间扩展方法(三)

分享特殊时间获取的扩展方法,包括当天开始/结束时间、当前周/月/季度/年的第一天和最后一天等,附代码示例和单元测试,库将上传至Nuget,源码在代码库。书接上回,我们继续来分享一些关于特殊时间获取的常用扩展方法。01、获取当天的开始时间 当天的开始时间指00:00:00时刻,…