diff --git a/C#/source/Yitter.IdGenerator/Contract/IIdGenerator.cs b/C#/source/Yitter.IdGenerator/Contract/IIdGenerator.cs index 275b3ed..a6f9f19 100644 --- a/C#/source/Yitter.IdGenerator/Contract/IIdGenerator.cs +++ b/C#/source/Yitter.IdGenerator/Contract/IIdGenerator.cs @@ -5,25 +5,34 @@ * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 * - */ - -using System; - -namespace Yitter.IdGenerator -{ - public interface IIdGenerator - { + */ + +using System; + +namespace Yitter.IdGenerator +{ + public interface IIdGenerator + { /// /// 生成过程中产生的事件 - /// + /// //Action GenIdActionAsync { get; set; } - + /// /// 生成新的long型Id /// - /// - long NewLong(); - - // Guid NewGuid(); - } -} + /// + long NewLong(); + + // Guid NewGuid(); + + + /// + /// 生成一个指定时间的 Id(时间用于构造时间差部分) + /// + /// 用于生成ID的时间(建议UTC或能正确转换为UTC的本地时间) + /// 是否是毫秒级的时间 + /// + long NextId(DateTime dateTime, bool isMillisecondPrecision = true); + } +} diff --git a/C#/source/Yitter.IdGenerator/Contract/ISnowWorker.cs b/C#/source/Yitter.IdGenerator/Contract/ISnowWorker.cs index 93d1b19..4760c8e 100644 --- a/C#/source/Yitter.IdGenerator/Contract/ISnowWorker.cs +++ b/C#/source/Yitter.IdGenerator/Contract/ISnowWorker.cs @@ -18,5 +18,13 @@ internal interface ISnowWorker //Action GenAction { get; set; } long NextId(); + + /// + /// 根据传入时间生成一个新的 Id + /// + /// + /// + /// + long NextId(DateTime dateTime, bool isMillisecondPrecision); } } diff --git a/C#/source/Yitter.IdGenerator/Core/SnowWorkerM1.cs b/C#/source/Yitter.IdGenerator/Core/SnowWorkerM1.cs index 07a0040..09f187c 100644 --- a/C#/source/Yitter.IdGenerator/Core/SnowWorkerM1.cs +++ b/C#/source/Yitter.IdGenerator/Core/SnowWorkerM1.cs @@ -5,27 +5,27 @@ * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 * - */ - -using System; -using System.Threading; + */ + +using System; +using System.Threading; using System.Threading.Tasks; -namespace Yitter.IdGenerator -{ - /// - /// 雪花漂移算法 - /// - internal class SnowWorkerM1 : ISnowWorker +namespace Yitter.IdGenerator +{ + /// + /// 雪花漂移算法 + /// + internal class SnowWorkerM1 : ISnowWorker { /// /// 基础时间 /// - protected readonly DateTime BaseTime; + protected readonly DateTime BaseTime; - /// - /// 机器码 - /// + /// + /// 机器码 + /// protected readonly ushort WorkerId = 0; /// @@ -40,7 +40,7 @@ internal class SnowWorkerM1 : ISnowWorker /// /// 最大序列数(含) - /// + /// protected readonly int MaxSeqNumber = 0; /// @@ -57,13 +57,13 @@ internal class SnowWorkerM1 : ISnowWorker protected static object _SyncLock = new object(); protected ushort _CurrentSeqNumber = 0; - protected long _LastTimeTick = 0; // -1L - protected long _TurnBackTimeTick = 0; // -1L; - protected byte _TurnBackIndex = 0; - protected bool _IsOverCost = false; + protected long _LastTimeTick = 0; // -1L + protected long _TurnBackTimeTick = 0; // -1L; + protected byte _TurnBackIndex = 0; + protected bool _IsOverCost = false; protected int _OverCostCountInOneTerm = 0; - -#if DEBUG + +#if DEBUG protected int _GenCountInOneTerm = 0; protected int _TermIndex = 0; #endif @@ -73,8 +73,8 @@ internal class SnowWorkerM1 : ISnowWorker //private static long _StartTimeTick = 0; //private static long _BaseTimeTick = 0; - - public SnowWorkerM1(IdGeneratorOptions options) + + public SnowWorkerM1(IdGeneratorOptions options) { // 1.BaseTime if (options.BaseTime != DateTime.MinValue) @@ -119,17 +119,17 @@ public SnowWorkerM1(IdGeneratorOptions options) MinSeqNumber = options.MinSeqNumber; // 7.Others - TopOverCostCount = options.TopOverCostCount; + TopOverCostCount = options.TopOverCostCount; //if (TopOverCostCount == 0) //{ // TopOverCostCount = 2000; //} - _TimestampShift = (byte)(WorkerIdBitLength + SeqBitLength); - _CurrentSeqNumber = options.MinSeqNumber; - - //_BaseTimeTick = BaseTime.Ticks; - //_StartTimeTick = (long)(DateTime.UtcNow.Subtract(BaseTime).TotalMilliseconds) - Environment.TickCount; + _TimestampShift = (byte)(WorkerIdBitLength + SeqBitLength); + _CurrentSeqNumber = options.MinSeqNumber; + + //_BaseTimeTick = BaseTime.Ticks; + //_StartTimeTick = (long)(DateTime.UtcNow.Subtract(BaseTime).TotalMilliseconds) - Environment.TickCount; } #if DEBUG @@ -140,7 +140,7 @@ private void DoGenIdAction(OverCostActionArg arg) { GenAction(arg); }); - } + } private void BeginOverCostAction(in long useTimeTick) { @@ -265,7 +265,7 @@ protected virtual long NextOverCostId() return CalcId(_LastTimeTick); } - protected virtual long NextNormalId() + protected virtual long NextNormalId() { long currentTimeTick = GetCurrentTimeTick(); @@ -346,11 +346,11 @@ protected virtual long CalcTurnBackId(long useTimeTick) return result; } - protected virtual long GetCurrentTimeTick() - { - //return (long)(DateTime.UtcNow - BaseTime).Ticks; - //return (long)(_StartTimeTick + Environment.TickCount); - return (long)(DateTime.UtcNow - BaseTime).TotalMilliseconds; + protected virtual long GetCurrentTimeTick() + { + //return (long)(DateTime.UtcNow - BaseTime).Ticks; + //return (long)(_StartTimeTick + Environment.TickCount); + return (long)(DateTime.UtcNow - BaseTime).TotalMilliseconds; } protected virtual long GetNextTimeTick() @@ -374,6 +374,81 @@ public virtual long NextId() { return _IsOverCost ? NextOverCostId() : NextNormalId(); } - } - } -} + } + + + // 新增:用于历史发号的独立序列计数器 + protected int _historySeqCounter = 0; + + /// + /// + /// + /// + /// + /// + /// + public virtual long NextId(DateTime dateTime, bool isMillisecondPrecision) + { + // 如果没有毫秒精度,则用当前系统时间的毫秒值覆盖 + if (!isMillisecondPrecision) + { + var nowMs = DateTime.UtcNow.Millisecond; // 0..999 + dateTime = new DateTime( + dateTime.Year, dateTime.Month, dateTime.Day, + dateTime.Hour, dateTime.Minute, dateTime.Second, + nowMs, dateTime.Kind + ); + } + + // 计算与基准时间的毫秒差 + var timeTick = ToTimeTick(dateTime); + if (timeTick < 0) + throw new ArgumentOutOfRangeException(nameof(dateTime), "指定时间早于 BaseTime。"); + if (timeTick > GetMaxTimeTick()) + throw new ArgumentOutOfRangeException(nameof(dateTime), "指定时间超过时间位宽上限。"); + + // 历史路径不影响实时状态:使用独立序列 + var half = (MaxSeqNumber + 1) / 2; + var localSeq = Interlocked.Increment(ref _historySeqCounter); + var seq = half + (localSeq & (half - 1)); + if (seq > MaxSeqNumber) seq &= MaxSeqNumber; + + return CalcId(timeTick, seq); // 按时间差+WorkerId+序列号拼装:contentReference[oaicite:2]{index=2} + } + + /// + /// 获取时间戳(相对于 BaseTime 的毫秒时间戳) + /// 将dateTime转换为UTC时间,并计算与BaseTime的毫秒差。 + /// + /// + /// + protected long ToTimeTick(DateTime dateTime) + { + // 以 BaseTime(毫秒时间戳)为基准换算 + return (long)(dateTime.ToUniversalTime() - BaseTime).TotalMilliseconds; + } + + /// + /// 获取最大时间戳(时间位宽上限) + /// + /// + protected long GetMaxTimeTick() + { + int timestampBits = 63 - WorkerIdBitLength - SeqBitLength; + return (timestampBits >= 63) ? long.MaxValue : ((1L << timestampBits) - 1); + } + + /// + /// + /// + /// + /// + /// + public virtual long CalcId(long timeTick, long seq) + { + return ((timeTick << _TimestampShift) + + ((long)WorkerId << SeqBitLength) + + (uint)seq); + } + } +} diff --git a/C#/source/Yitter.IdGenerator/Core/SnowWorkerM3.cs b/C#/source/Yitter.IdGenerator/Core/SnowWorkerM3.cs index 4ad78a7..4683713 100644 --- a/C#/source/Yitter.IdGenerator/Core/SnowWorkerM3.cs +++ b/C#/source/Yitter.IdGenerator/Core/SnowWorkerM3.cs @@ -5,18 +5,18 @@ * 版权说明:只要保留本版权,你可以免费使用、修改、分发本代码。 * 免责条款:任何因为本代码产生的系统、法律、政治、宗教问题,均与版权所有者无关。 * - */ - -using System; -using System.Threading; + */ + +using System; +using System.Threading; using System.Threading.Tasks; -namespace Yitter.IdGenerator -{ - /// - /// 雪花漂移算法(支持数据中心ID和秒级时间戳) - /// - internal class SnowWorkerM3 : SnowWorkerM1 +namespace Yitter.IdGenerator +{ + /// + /// 雪花漂移算法(支持数据中心ID和秒级时间戳) + /// + internal class SnowWorkerM3 : SnowWorkerM1 { /// /// 数据中心ID(默认0) @@ -32,22 +32,22 @@ internal class SnowWorkerM3 : SnowWorkerM1 /// 时间戳类型(0-毫秒,1-秒),默认0 /// protected readonly byte TimestampType = 0; - - - public SnowWorkerM3(IdGeneratorOptions options) : base(options) + + + public SnowWorkerM3(IdGeneratorOptions options) : base(options) { // 秒级时间戳类型 TimestampType = options.TimestampType; // DataCenter相关 - DataCenterId = options.DataCenterId; + DataCenterId = options.DataCenterId; DataCenterIdBitLength = options.DataCenterIdBitLength; if (TimestampType == 1) { TopOverCostCount = 0; } - _TimestampShift = (byte)(DataCenterIdBitLength + WorkerIdBitLength + SeqBitLength); + _TimestampShift = (byte)(DataCenterIdBitLength + WorkerIdBitLength + SeqBitLength); } protected override long CalcId(long useTimeTick) @@ -72,12 +72,71 @@ protected override long CalcTurnBackId(long useTimeTick) return result; } - protected override long GetCurrentTimeTick() - { + protected override long GetCurrentTimeTick() + { return TimestampType == 0 ? (long)(DateTime.UtcNow - BaseTime).TotalMilliseconds : - (long)(DateTime.UtcNow - BaseTime).TotalSeconds; - } - - } -} + (long)(DateTime.UtcNow - BaseTime).TotalSeconds; + } + + /// + /// 新增方法:根据传入时间生成一个新的 ID + /// 如果 isMillisecondPrecision 为 false,则用当前时间的毫秒覆盖传入时间的毫秒 + /// + /// + /// + /// + /// + public override long NextId(DateTime dateTime, bool isMillisecondPrecision) + { + // 1. 统一时间到 UTC + var dtUtc = dateTime.Kind == DateTimeKind.Utc ? dateTime : dateTime.ToUniversalTime(); + + // 2. 如果没有毫秒精度,用当前毫秒覆盖 + if (!isMillisecondPrecision) + { + int currentMs = DateTime.UtcNow.Millisecond; // 0..999 + dtUtc = new DateTime( + dtUtc.Year, dtUtc.Month, dtUtc.Day, + dtUtc.Hour, dtUtc.Minute, dtUtc.Second, + currentMs, + DateTimeKind.Utc); + } + + // 3. 计算与 BaseTime 的时间差(毫秒或秒) + long timeTick; + if (TimestampType == 0) + { + // 毫秒级时间戳 + timeTick = (long)(dtUtc - BaseTime).TotalMilliseconds; + } + else + { + // 秒级时间戳 + timeTick = (long)(dtUtc - BaseTime).TotalSeconds; + } + + // 如果时间差为负,说明传入时间早于 BaseTime,按需求可以抛异常或调整,这里抛异常 + if (timeTick < 0) + { + throw new ArgumentOutOfRangeException(nameof(dateTime), "指定时间早于 BaseTime,无法生成 ID。"); + } + + // 4. 生成独立序列号:取序列空间上半段,避免与实时发号序列冲突 + long half = (MaxSeqNumber + 1) / 2; + long localSeq = Interlocked.Increment(ref _historySeqCounter); + long seq = half + (localSeq & (half - 1)); + if (seq > MaxSeqNumber) seq &= MaxSeqNumber; + + // 5. 拼装 ID:时间戳部分左移 _TimestampShift 位,随后是 DataCenterId 和 WorkerId + // 与 CalcId 的实现保持一致,但不修改任何实时状态 + long id = (timeTick << _TimestampShift) + + ((long)DataCenterId << DataCenterIdBitLength) + + ((long)WorkerId << SeqBitLength) + + seq; + + return id; + } + + } +} diff --git a/C#/source/Yitter.IdGenerator/DefaultIdGenerator.cs b/C#/source/Yitter.IdGenerator/DefaultIdGenerator.cs index ea4e2a9..d94e844 100644 --- a/C#/source/Yitter.IdGenerator/DefaultIdGenerator.cs +++ b/C#/source/Yitter.IdGenerator/DefaultIdGenerator.cs @@ -126,5 +126,16 @@ public long NewLong() { return _SnowWorker.NextId(); } + + /// + /// 生成一个指定时间的 Id(时间用于构造时间差部分) + /// + /// 用于生成ID的时间(建议UTC或能正确转换为UTC的本地时间) + /// 是否是毫秒级的时间 + /// + public long NextId(DateTime dateTime, bool isMillisecondPrecision = true) + { + return _SnowWorker.NextId(dateTime, isMillisecondPrecision); + } } }