@@ -11,18 +11,130 @@ namespace SciSharp.MySQL.Replication.Types
11
11
/// <remarks>
12
12
/// Handles the reading and conversion of MySQL TIME values with fractional seconds.
13
13
/// </remarks>
14
- class TimeV2Type : IMySQLDataType
14
+ class TimeV2Type : IMySQLDataType , IColumnMetadataLoader
15
15
{
16
+ /// <summary>
17
+ /// Loads the fractional seconds precision from the column metadata.
18
+ /// </summary>
19
+ /// <param name="columnMetadata">The column metadata containing the precision value.</param>
20
+ public void LoadMetadataValue ( ColumnMetadata columnMetadata )
21
+ {
22
+ // For TIME2 type, the metadata value represents the precision of fractional seconds
23
+ // MySQL supports precision values from 0 to 6 (microsecond precision)
24
+ columnMetadata . Options = new TimeV2Options
25
+ {
26
+ FractionalSecondsPrecision = columnMetadata . MetadataValue [ 0 ]
27
+ } ;
28
+ }
29
+
16
30
/// <summary>
17
31
/// Reads a TIME2 value from the binary log.
18
32
/// </summary>
19
33
/// <param name="reader">The sequence reader containing the bytes to read.</param>
20
34
/// <param name="columnMetadata">Metadata for the column defining fractional second precision.</param>
21
- /// <returns>An object representing the MySQL TIME2 value.</returns>
22
- /// <exception cref="NotImplementedException">This method has not yet been implemented.</exception>
35
+ /// <returns>A TimeSpan representing the MySQL TIME2 value.</returns>
23
36
public object ReadValue ( ref SequenceReader < byte > reader , ColumnMetadata columnMetadata )
24
37
{
25
- throw new NotImplementedException ( ) ;
38
+ // Get the fractional seconds precision from metadata (0-6)
39
+ int fsp = columnMetadata . Options is TimeV2Options options ? options . FractionalSecondsPrecision : 0 ;
40
+
41
+ // Read the integer part (3 bytes)
42
+ byte intPartByte1 , intPartByte2 , intPartByte3 ;
43
+ reader . TryRead ( out intPartByte1 ) ;
44
+ reader . TryRead ( out intPartByte2 ) ;
45
+ reader . TryRead ( out intPartByte3 ) ;
46
+
47
+ // Combine into a 24-bit integer, bigendian
48
+ int intPart = ( intPartByte1 << 16 ) | ( intPartByte2 << 8 ) | intPartByte3 ;
49
+
50
+ // In MySQL 5.6.4+ TIME2 format:
51
+ // Bit 1 (MSB): Sign bit (1=negative, 0=positive)
52
+ // Bits 2-24: Packed BCD encoding of time value
53
+ bool isNegative = ( ( intPart & 0x800000 ) == 0 ) ; // In MySQL TIME2, 0 is negative, 1 is positive
54
+
55
+ // If negative, apply two's complement
56
+ if ( isNegative )
57
+ {
58
+ intPart = ~ intPart + 1 ;
59
+ intPart &= 0x7FFFFF ; // Keep only the 23 bits for the absolute value
60
+ }
61
+
62
+ // TIME2 is packed in a special format:
63
+ // Bits 2-13: Hours (12 bits)
64
+ // Bits 14-19: Minutes (6 bits)
65
+ // Bits 20-25: Seconds (6 bits)
66
+ int hours = ( intPart >> 12 ) & 0x3FF ;
67
+ int minutes = ( intPart >> 6 ) & 0x3F ;
68
+ int seconds = intPart & 0x3F ;
69
+
70
+ // Read fractional seconds if precision > 0
71
+ int microseconds = 0 ;
72
+ if ( fsp > 0 )
73
+ {
74
+ // Calculate bytes needed for the requested precision
75
+ int fractionalBytes = ( fsp + 1 ) / 2 ;
76
+ int fraction = 0 ;
77
+
78
+ // Read bytes for fractional seconds
79
+ for ( int i = 0 ; i < fractionalBytes ; i ++ )
80
+ {
81
+ byte b ;
82
+ reader . TryRead ( out b ) ;
83
+ fraction = ( fraction << 8 ) | b ;
84
+ }
85
+
86
+ // Convert to microseconds based on precision
87
+ int scaleFactor = 1000000 ;
88
+ switch ( fsp )
89
+ {
90
+ case 1 : scaleFactor = 100000 ; break ;
91
+ case 2 : scaleFactor = 10000 ; break ;
92
+ case 3 : scaleFactor = 1000 ; break ;
93
+ case 4 : scaleFactor = 100 ; break ;
94
+ case 5 : scaleFactor = 10 ; break ;
95
+ case 6 : scaleFactor = 1 ; break ;
96
+ }
97
+
98
+ microseconds = fraction * scaleFactor ;
99
+ }
100
+
101
+ // Create TimeSpan
102
+ TimeSpan result ;
103
+
104
+ if ( hours >= 24 )
105
+ {
106
+ // For large hour values, convert to days + remaining hours
107
+ int days = hours / 24 ;
108
+ int remainingHours = hours % 24 ;
109
+
110
+ // Create TimeSpan with days, hours, minutes, seconds, ms
111
+ result = new TimeSpan ( days , remainingHours , minutes , seconds , microseconds / 1000 ) ;
112
+
113
+ // Add remaining microseconds as ticks (1 tick = 100 nanoseconds, 1 microsecond = 10 ticks)
114
+ if ( microseconds % 1000 > 0 )
115
+ {
116
+ result = result . Add ( TimeSpan . FromTicks ( ( microseconds % 1000 ) * 10 ) ) ;
117
+ }
118
+ }
119
+ else
120
+ {
121
+ // Standard case for hours < 24
122
+ result = new TimeSpan ( 0 , hours , minutes , seconds , microseconds / 1000 ) ;
123
+
124
+ // Add microsecond precision as ticks
125
+ if ( microseconds % 1000 > 0 )
126
+ {
127
+ result = result . Add ( TimeSpan . FromTicks ( ( microseconds % 1000 ) * 10 ) ) ;
128
+ }
129
+ }
130
+
131
+ // Apply sign
132
+ return isNegative ? result . Negate ( ) : result ;
26
133
}
27
134
}
135
+
136
+ class TimeV2Options
137
+ {
138
+ public int FractionalSecondsPrecision { get ; set ; } = 0 ;
139
+ }
28
140
}
0 commit comments