以下代码用于生成有序的GUID字符串。
使用方法为:new OrderedGUIDGenerator().NewGuid()即可生成根据时间排序的Guid字符串。
/**
* 用于生成根据时间戳附加随机数确定的有序GUID值
* 使用方法:var orderedGuid=new OrderedGUIDGenerator().newGuid()。如果将newGuid()方法改为静态方法,即加上static,则使用方法为var orderedGuid=OrderedGUIDGenerator.newGuid()
*/
export default class OrderedGUIDGenerator
{
//生成的Guid的格式为:dddddddd-dddd-Mddd-Ndrr-rrrrrrrrrrrr
// - M = RFC 版本(version), 这里取为4
// - N = RFC 变体(variant),这里固定为8
// - d = 从公元1年1月1日0时至今的时间总长(以100ns为单位)
// - r = 随机数(random bytes)
private static readonly DIFF:bigint=62135596800000000000n; //从公元1年1月1日0时0分0秒到公元1970年1月1日0时0分0秒的纳秒数
//nodeVitualStartTime表示node运行时启动时的纳秒数(从公元1年1月1日0时0分0秒算起)。资料显示,process.hrtime.bigint()方法计算的是方法调用时刻与node启动时的时间差(ns)
private static readonly nodeVitualStartTime:bigint = BigInt(Date.now()) * 1000000n+OrderedGUIDGenerator.DIFF-process.hrtime.bigint();
private static readonly version = 4;//RFC版本号M,这里用4
private static readonly variant = 8; //RFC变体N,这里用8
private static readonly filterHighBit = 0b00001111;
private static readonly filterLowBit = 0b11110000;
//获取从公元1年1月1日0时0分0秒开始的纳秒数
private static nanoTime():bigint {return OrderedGUIDGenerator.nodeVitualStartTime+process.hrtime.bigint()};
/***********
* 用于生成Guid字符串的方法
* @param 无参数
*/
public static newGuid():string {
let nanoSeconds=this.nanoTime();
const ticks:bigint=nanoSeconds/100n; //获得以100ns为单位的时间
//不再使用下面这句,因为测试过程中经常只是00,后续直接用随机数来代替
// const nanoEnds:bigint=nanoSeconds%100n; //100ns的尾数(即nanaSconds的最后两位数字,也就是十位数+个位数),用于填充guidBytes数组的第10个元素
let guidBytes:string[] = [];//guid数组,共有16个元素
let ticksHex=ticks.toString(16).padStart(16,"0");//将ticks数据转换为16进制字符串,转换后应该有16字节,若不足16字节,头部用0填充
for(let i=0;i<6;i++) //guidBytes【0】~guidBytes【5】
{
guidBytes.push(ticksHex.substring(i*2,i*2+2))
}
let ticksHexForUintPos6=ticksHex.substring(12,14);
let ticksHexForUintPos7=ticksHex.substring(14,16);
let timeStampArrayPos6=parseInt(ticksHexForUintPos6,16),timeStampArrayPos7=parseInt(ticksHexForUintPos7,16);
//以下三行代码通过移位计算确定第7-第9个字节的数据
guidBytes.push(((OrderedGUIDGenerator.version<<4)|((timeStampArrayPos6&OrderedGUIDGenerator.filterLowBit)>>4)).toString(16).padStart(2,'0')); //guidBytes【6】 ; 高4位为版本 | 低4位取时间戳序号[6]的元素的高4位
guidBytes.push((((timeStampArrayPos6&OrderedGUIDGenerator.filterHighBit)<<4)|((timeStampArrayPos7&OrderedGUIDGenerator.filterLowBit)>>4)).toString(16).padStart(2,'0')); //guidBytes【7】 ;高4位取:[6]低4位 | 低4位取:[7]高4位
guidBytes.push(((OrderedGUIDGenerator.variant<<4)|(timeStampArrayPos7&OrderedGUIDGenerator.filterHighBit)).toString(16).padStart(2,'0')); // guidBytes【8】;高4位为:变体 | 低4位取:[7]低4位
//第10个字节采用nanosecond%100,即最后两位数字的值来填充
//guidBytes.push(Number(nanoEnds).toString(16).padStart(2,'0')); // guidBytes[9]
//以下这段代码通过随机数填充guidBytes尾部第10-16个元素(共14个字符,可以填充7个元素,即guidBytes[9]~guidBytes[15])
//***以下这句代码生成14个字符的随机字符串,由于一次random()的字符数可能不足14个,所以用2次,并通过substring(2)去掉头部的两个字符"0."
let randomString= (Math.random().toString(16).substring(2)+Math.random().toString(16).substring(2)).padStart(14,'0').substring(0,14);
for(let i=0;i<7;i++) //将生成的字符复制到guidBytes数组的尾部7个元素位置 guidBytes[9]~guidBytes[15]
{
let randomUnit8Str=randomString.substring(2*i,2*i+2);
guidBytes.push(randomUnit8Str); // guidBytes[9]~guidBytes[15]
}
let g=guidBytes; //此处不必要,仅仅是用短的变量g,以便于下一句字符串拼接起来少写点字符,一行能放下
let guidStr=g[0]+g[1]+g[2]+g[3]+"-"+g[4]+g[5]+"-"+g[6]+g[7]+"-"+g[8]+g[9]+"-"+g[10]+g[11]+g[12]+g[13]+g[14]+g[15];
return guidStr;
};
}