Skip to content

Commit db915a6

Browse files
committed
added timestamp types
1 parent e666680 commit db915a6

File tree

4 files changed

+96
-9
lines changed

4 files changed

+96
-9
lines changed

src/SciSharp.MySQL.Replication/Events/LogEvent.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ static LogEvent()
7070
DataTypes[(int)ColumnType.DATETIME_V2] = new DateTimeV2Type();
7171
DataTypes[(int)ColumnType.TIME] = new TimeType();
7272
DataTypes[(int)ColumnType.TIME_V2] = new TimeV2Type();
73-
//DataTypes[(int)ColumnType.TIMESTAMP] = new TimestampType();
73+
DataTypes[(int)ColumnType.TIMESTAMP] = new TimestampType();
74+
DataTypes[(int)ColumnType.TIMESTAMP_V2] = new TimestampV2Type();
7475
DataTypes[(int)ColumnType.ENUM] = new EnumType();
7576
DataTypes[(int)ColumnType.SET] = new SetType();
7677
}

src/SciSharp.MySQL.Replication/Types/TimestampType.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ namespace SciSharp.MySQL.Replication.Types
1313
/// </remarks>
1414
class TimestampType : IMySQLDataType
1515
{
16+
// Unix epoch start for MySQL TIMESTAMP (1970-01-01 00:00:00 UTC)
17+
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
18+
1619
/// <summary>
1720
/// Reads a TIMESTAMP value from the binary log.
1821
/// </summary>
@@ -21,7 +24,29 @@ class TimestampType : IMySQLDataType
2124
/// <returns>A DateTime object representing the MySQL TIMESTAMP value.</returns>
2225
public object ReadValue(ref SequenceReader<byte> reader, ColumnMetadata columnMetadata)
2326
{
24-
return new DateTime(reader.ReadLong(4) * 1000);
27+
// MySQL TIMESTAMP is stored as a 4-byte integer
28+
// representing seconds since Unix epoch (1970-01-01 00:00:00 UTC)
29+
30+
// Read the 4 bytes as a 32-bit unsigned integer
31+
uint secondsSinceEpoch = 0;
32+
33+
// Read each byte individually
34+
reader.TryRead(out byte b0);
35+
reader.TryRead(out byte b1);
36+
reader.TryRead(out byte b2);
37+
reader.TryRead(out byte b3);
38+
39+
// Combine bytes to form the 32-bit unsigned int (big-endian)
40+
secondsSinceEpoch = ((uint)b0 << 24) |
41+
((uint)b1 << 16) |
42+
((uint)b2 << 8) |
43+
b3;
44+
45+
// Convert Unix timestamp to DateTime
46+
// MySQL stores TIMESTAMP in UTC, so we return it as UTC DateTime
47+
DateTime timestamp = UnixEpoch.AddSeconds(secondsSinceEpoch);
48+
49+
return timestamp;
2550
}
2651
}
2752
}

src/SciSharp.MySQL.Replication/Types/TimestampV2Type.cs

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,24 @@ namespace SciSharp.MySQL.Replication.Types
1111
/// <remarks>
1212
/// Handles the reading and conversion of MySQL TIMESTAMP values with fractional seconds.
1313
/// </remarks>
14-
class TimestampV2Type : TimeBaseType, IMySQLDataType
14+
class TimestampV2Type : TimeBaseType, IMySQLDataType, IColumnMetadataLoader
1515
{
16+
// Unix epoch start for MySQL TIMESTAMP (1970-01-01 00:00:00 UTC)
17+
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
18+
19+
/// <summary>
20+
/// Loads metadata for TIMESTAMP2 type.
21+
/// </summary>
22+
/// <param name="columnMetadata">The column metadata object.</param>
23+
public void LoadMetadataValue(ColumnMetadata columnMetadata)
24+
{
25+
// For TIMESTAMP2, the metadata value represents the fractional seconds precision (0-6)
26+
columnMetadata.Options = new TimestampV2Options
27+
{
28+
FractionalSecondsPrecision = columnMetadata.MetadataValue[0]
29+
};
30+
}
31+
1632
/// <summary>
1733
/// Reads a TIMESTAMP2 value from the binary log.
1834
/// </summary>
@@ -21,11 +37,56 @@ class TimestampV2Type : TimeBaseType, IMySQLDataType
2137
/// <returns>A DateTime object representing the MySQL TIMESTAMP2 value.</returns>
2238
public object ReadValue(ref SequenceReader<byte> reader, ColumnMetadata columnMetadata)
2339
{
24-
int meta = columnMetadata.MetadataValue[0];
25-
var millis = (long)reader.ReadBigEndianInteger(4);
26-
var fsp = ReadFractionalSeconds(ref reader, meta);
27-
var ticks = millis * 1000 + fsp / 1000;
28-
return new DateTime(ticks);
40+
// Get the precision from metadata
41+
int fsp = columnMetadata.Options is TimestampV2Options options ?
42+
options.FractionalSecondsPrecision : 0;
43+
44+
// Read the 4-byte seconds part (big-endian integer representing seconds since Unix epoch)
45+
byte b0, b1, b2, b3;
46+
reader.TryRead(out b0);
47+
reader.TryRead(out b1);
48+
reader.TryRead(out b2);
49+
reader.TryRead(out b3);
50+
51+
uint secondsSinceEpoch = ((uint)b0 << 24) | ((uint)b1 << 16) | ((uint)b2 << 8) | b3;
52+
53+
// Start with the base DateTime (seconds part)
54+
DateTime timestamp = UnixEpoch.AddSeconds(secondsSinceEpoch);
55+
56+
// Read fractional seconds if precision > 0
57+
if (fsp > 0)
58+
{
59+
int fractionalValue = ReadFractionalSeconds(ref reader, fsp);
60+
61+
// Calculate microseconds
62+
int microseconds = 0;
63+
switch (fsp)
64+
{
65+
case 1: microseconds = fractionalValue * 100000; break;
66+
case 2: microseconds = fractionalValue * 10000; break;
67+
case 3: microseconds = fractionalValue * 1000; break;
68+
case 4: microseconds = fractionalValue * 100; break;
69+
case 5: microseconds = fractionalValue * 10; break;
70+
case 6: microseconds = fractionalValue; break;
71+
}
72+
73+
// Add microsecond precision to the DateTime
74+
// 1 tick = 100 nanoseconds, 1 microsecond = 10 ticks
75+
timestamp = timestamp.AddTicks(microseconds * 10);
76+
}
77+
78+
return timestamp;
2979
}
3080
}
81+
82+
/// <summary>
83+
/// Options specific to TIMESTAMP2 data type.
84+
/// </summary>
85+
class TimestampV2Options
86+
{
87+
/// <summary>
88+
/// Gets or sets the precision of fractional seconds (0-6).
89+
/// </summary>
90+
public int FractionalSecondsPrecision { get; set; }
91+
}
3192
}

tests/Test/DataTypesTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ await TestDataType<TimeSpan>("time_table", currentValue, currentValue.Add(new Ti
149149
});
150150
}
151151

152-
//[Fact]
152+
[Fact]
153153
public async Task TestTimestampType()
154154
{
155155
var currentValue = DateTime.UtcNow;

0 commit comments

Comments
 (0)