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);
+ }
}
}