@@ -11,18 +11,130 @@ namespace SciSharp.MySQL.Replication.Types
1111 /// <remarks>
1212 /// Handles the reading and conversion of MySQL TIME values with fractional seconds.
1313 /// </remarks>
14- class TimeV2Type : IMySQLDataType
14+ class TimeV2Type : IMySQLDataType , IColumnMetadataLoader
1515 {
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+
1630 /// <summary>
1731 /// Reads a TIME2 value from the binary log.
1832 /// </summary>
1933 /// <param name="reader">The sequence reader containing the bytes to read.</param>
2034 /// <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>
2336 public object ReadValue ( ref SequenceReader < byte > reader , ColumnMetadata columnMetadata )
2437 {
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 ;
26133 }
27134 }
135+
136+ class TimeV2Options
137+ {
138+ public int FractionalSecondsPrecision { get ; set ; } = 0 ;
139+ }
28140}
0 commit comments