|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Buffers.Binary;
using System.Collections;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
namespace System.Xml
{
// This is mostly just a copy of code in SqlTypes.SqlDecimal
internal struct BinXmlSqlDecimal
{
internal byte m_bLen;
internal byte m_bPrec;
internal byte m_bScale;
internal byte m_bSign;
internal uint m_data1;
internal uint m_data2;
internal uint m_data3;
internal uint m_data4;
public bool IsPositive
{
get
{
return (m_bSign == 0);
}
}
private const byte s_NUMERIC_MAX_PRECISION = 38; // Maximum precision of numeric
private const byte s_maxPrecision = s_NUMERIC_MAX_PRECISION; // max SS precision
private const int s_cNumeMax = 4;
internal const ulong x_llMax = long.MaxValue; // Max of Int64
public BinXmlSqlDecimal(byte[] data, int offset, bool trim)
{
byte b = data[offset];
m_bLen = b switch
{
7 => 1,
11 => 2,
15 => 3,
19 => 4,
_ => throw new XmlException(SR.XmlBinary_InvalidSqlDecimal, (string[]?)null),
};
m_bPrec = data[offset + 1];
m_bScale = data[offset + 2];
m_bSign = 0 == data[offset + 3] ? (byte)1 : (byte)0;
m_data1 = UIntFromByteArray(data, offset + 4);
m_data2 = (m_bLen > 1) ? UIntFromByteArray(data, offset + 8) : 0;
m_data3 = (m_bLen > 2) ? UIntFromByteArray(data, offset + 12) : 0;
m_data4 = (m_bLen > 3) ? UIntFromByteArray(data, offset + 16) : 0;
if (m_bLen == 4 && m_data4 == 0)
m_bLen = 3;
if (m_bLen == 3 && m_data3 == 0)
m_bLen = 2;
if (m_bLen == 2 && m_data2 == 0)
m_bLen = 1;
AssertValid();
if (trim)
{
TrimTrailingZeros();
AssertValid();
}
}
private static uint UIntFromByteArray(byte[] data, int offset)
=> BinaryPrimitives.ReadUInt32LittleEndian(data.AsSpan(offset));
// Multi-precision one super-digit divide in place.
// U = U / D,
// R = U % D
// Length of U can decrease
private static void MpDiv1(uint[] rgulU, // InOut| U
ref int ciulU, // InOut| # of digits in U
uint iulD, // In | D
out uint iulR // Out | R
)
{
Debug.Assert(rgulU.Length == s_cNumeMax);
uint ulCarry = 0;
ulong dwlAccum;
ulong ulD = (ulong)iulD;
int idU = ciulU;
Debug.Assert(iulD != 0, "iulD != 0", "Divided by zero!");
Debug.Assert(iulD > 0, "iulD > 0", "Invalid data: less than zero");
Debug.Assert(ciulU > 0, "ciulU > 0", "No data in the array");
while (idU > 0)
{
idU--;
dwlAccum = (((ulong)ulCarry) << 32) + (ulong)(rgulU[idU]);
rgulU[idU] = (uint)(dwlAccum / ulD);
ulCarry = (uint)(dwlAccum - (ulong)rgulU[idU] * ulD); // (ULONG) (dwlAccum % iulD)
}
iulR = ulCarry;
MpNormalize(rgulU, ref ciulU);
}
// Normalize multi-precision number - remove leading zeroes
private static void MpNormalize(uint[] rgulU, // In | Number
ref int ciulU // InOut| # of digits
)
{
while (ciulU > 1 && rgulU[ciulU - 1] == 0)
ciulU--;
}
//Determine the number of uints needed for a numeric given a precision
//Precision Length
// 0 invalid
// 1-9 1
// 10-19 2
// 20-28 3
// 29-38 4
private static ReadOnlySpan<byte> RgCLenFromPrec =>
[
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
];
private static byte CLenFromPrec(byte bPrec)
{
Debug.Assert(bPrec <= s_maxPrecision && bPrec > 0, "bPrec <= MaxPrecision && bPrec > 0", "Invalid numeric precision");
return RgCLenFromPrec[bPrec - 1];
}
private static char ChFromDigit(uint uiDigit)
{
Debug.Assert(uiDigit < 10);
return (char)(uiDigit + '0');
}
public decimal ToDecimal()
{
if ((int)m_data4 != 0 || m_bScale > 28)
throw new XmlException(SR.SqlTypes_ArithOverflow, (string?)null);
return new decimal((int)m_data1, (int)m_data2, (int)m_data3, !IsPositive, m_bScale);
}
private void TrimTrailingZeros()
{
uint[] rgulNumeric = new uint[4] { m_data1, m_data2, m_data3, m_data4 };
int culLen = m_bLen;
uint ulRem; //Remainder of a division by x_ulBase10, i.e.,least significant digit
// special-case 0
if (culLen == 1 && rgulNumeric[0] == 0)
{
m_bScale = 0;
return;
}
while (m_bScale > 0 && (culLen > 1 || rgulNumeric[0] != 0))
{
MpDiv1(rgulNumeric, ref culLen, 10, out ulRem);
if (ulRem == 0)
{
m_data1 = rgulNumeric[0];
m_data2 = rgulNumeric[1];
m_data3 = rgulNumeric[2];
m_data4 = rgulNumeric[3];
m_bScale--;
}
else
{
break;
}
}
if (m_bLen == 4 && m_data4 == 0)
m_bLen = 3;
if (m_bLen == 3 && m_data3 == 0)
m_bLen = 2;
if (m_bLen == 2 && m_data2 == 0)
m_bLen = 1;
}
public override string ToString()
{
AssertValid();
// Make local copy of data to avoid modifying input.
uint[] rgulNumeric = new uint[4] { m_data1, m_data2, m_data3, m_data4 };
int culLen = m_bLen;
Span<char> pszTmp = stackalloc char[s_NUMERIC_MAX_PRECISION + 1]; //Local Character buffer to hold
//the decimal digits, from the
//lowest significant to highest significant
int iDigits = 0; //Number of significant digits
uint ulRem; //Remainder of a division by x_ulBase10, i.e.,least significant digit
// Build the final numeric string by inserting the sign, reversing
// the order and inserting the decimal number at the correct position
//Retrieve each digit from the lowest significant digit
while (culLen > 1 || rgulNumeric[0] != 0)
{
MpDiv1(rgulNumeric, ref culLen, 10, out ulRem);
//modulo x_ulBase10 is the lowest significant digit
pszTmp[iDigits++] = ChFromDigit(ulRem);
}
// if scale of the number has not been
// reached pad remaining number with zeros.
while (iDigits <= m_bScale)
{
pszTmp[iDigits++] = ChFromDigit(0);
}
bool fPositive = IsPositive;
// Increment the result length if negative (need to add '-')
int uiResultLen = fPositive ? iDigits : iDigits + 1;
// Increment the result length if scale > 0 (need to add '.')
if (m_bScale > 0)
uiResultLen++;
Span<char> szResult = stackalloc char[uiResultLen];
int iCurChar = 0;
if (!fPositive)
szResult[iCurChar++] = '-';
while (iDigits > 0)
{
if (iDigits-- == m_bScale)
szResult[iCurChar++] = '.';
szResult[iCurChar++] = pszTmp[iDigits];
}
AssertValid();
return new string(szResult);
}
// Is this RE numeric valid?
[System.Diagnostics.Conditional("DEBUG")]
private void AssertValid()
{
// Scale,Prec in range
Debug.Assert(m_bScale <= s_NUMERIC_MAX_PRECISION, "m_bScale <= NUMERIC_MAX_PRECISION", "In AssertValid");
Debug.Assert(m_bScale <= m_bPrec, "m_bScale <= m_bPrec", "In AssertValid");
Debug.Assert(m_bScale >= 0, "m_bScale >= 0", "In AssertValid");
Debug.Assert(m_bPrec > 0, "m_bPrec > 0", "In AssertValid");
Debug.Assert(CLenFromPrec(m_bPrec) >= m_bLen, "CLenFromPrec(m_bPrec) >= m_bLen", "In AssertValid");
Debug.Assert(m_bLen <= s_cNumeMax, "m_bLen <= x_cNumeMax", "In AssertValid");
uint[] rglData = new uint[4] { m_data1, m_data2, m_data3, m_data4 };
// highest UI4 is non-0 unless value "zero"
if (rglData[m_bLen - 1] == 0)
{
Debug.Assert(m_bLen == 1, "m_bLen == 1", "In AssertValid");
}
// All UI4s from length to end are 0
for (int iulData = m_bLen; iulData < s_cNumeMax; iulData++)
Debug.Assert(rglData[iulData] == 0, "rglData[iulData] == 0", "In AssertValid");
}
}
internal readonly struct BinXmlSqlMoney
{
private readonly long _data;
public BinXmlSqlMoney(int v) { _data = v; }
public BinXmlSqlMoney(long v) { _data = v; }
public decimal ToDecimal()
{
bool neg;
ulong v;
if (_data < 0)
{
neg = true;
v = (ulong)unchecked(-_data);
}
else
{
neg = false;
v = (ulong)_data;
}
// SQL Server stores money8 as ticks of 1/10000.
const byte MoneyScale = 4;
return new decimal(unchecked((int)v), unchecked((int)(v >> 32)), 0, neg, MoneyScale);
}
public override string ToString()
{
decimal money = ToDecimal();
// Formatting of SqlMoney: At least two digits after decimal point
return money.ToString("#0.00##", CultureInfo.InvariantCulture);
}
}
internal abstract class BinXmlDateTime
{
private const int MaxFractionDigits = 7;
internal static ReadOnlySpan<int> KatmaiTimeScaleMultiplicator =>
[
10000000,
1000000,
100000,
10000,
1000,
100,
10,
1,
];
private static void Write2Dig(StringBuilder sb, int val)
{
Debug.Assert(val >= 0 && val < 100);
sb.Append((char)('0' + (val / 10)));
sb.Append((char)('0' + (val % 10)));
}
private static void Write4DigNeg(StringBuilder sb, int val)
{
Debug.Assert(val > -10000 && val < 10000);
if (val < 0)
{
val = -val;
sb.Append('-');
}
Write2Dig(sb, val / 100);
Write2Dig(sb, val % 100);
}
private static void Write3Dec(StringBuilder sb, int val)
{
Debug.Assert(val >= 0 && val < 1000);
int c3 = val % 10;
val /= 10;
int c2 = val % 10;
val /= 10;
int c1 = val;
sb.Append('.');
sb.Append((char)('0' + c1));
sb.Append((char)('0' + c2));
sb.Append((char)('0' + c3));
}
private static void WriteDate(StringBuilder sb, int yr, int mnth, int day)
{
Write4DigNeg(sb, yr);
sb.Append('-');
Write2Dig(sb, mnth);
sb.Append('-');
Write2Dig(sb, day);
}
private static void WriteTime(StringBuilder sb, int hr, int min, int sec, int ms)
{
Write2Dig(sb, hr);
sb.Append(':');
Write2Dig(sb, min);
sb.Append(':');
Write2Dig(sb, sec);
if (ms != 0)
{
Write3Dec(sb, ms);
}
}
private static void WriteTimeFullPrecision(StringBuilder sb, int hr, int min, int sec, int fraction)
{
Write2Dig(sb, hr);
sb.Append(':');
Write2Dig(sb, min);
sb.Append(':');
Write2Dig(sb, sec);
if (fraction != 0)
{
int fractionDigits = MaxFractionDigits;
while (fraction % 10 == 0)
{
fractionDigits--;
fraction /= 10;
}
Span<char> chars = stackalloc char[fractionDigits];
while (fractionDigits > 0)
{
fractionDigits--;
chars[fractionDigits] = (char)(fraction % 10 + '0');
fraction /= 10;
}
sb.Append('.');
sb.Append(chars);
}
}
private static void WriteTimeZone(StringBuilder sb, TimeSpan zone)
{
bool negTimeZone = true;
if (zone.Ticks < 0)
{
negTimeZone = false;
zone = zone.Negate();
}
WriteTimeZone(sb, negTimeZone, zone.Hours, zone.Minutes);
}
private static void WriteTimeZone(StringBuilder sb, bool negTimeZone, int hr, int min)
{
if (hr == 0 && min == 0)
{
sb.Append('Z');
}
else
{
sb.Append(negTimeZone ? '+' : '-');
Write2Dig(sb, hr);
sb.Append(':');
Write2Dig(sb, min);
}
}
private static void BreakDownXsdDateTime(long val, out int yr, out int mnth, out int day, out int hr, out int min, out int sec, out int ms)
{
if (val < 0)
goto Error;
long date = val / 4; // trim indicator bits
ms = (int)(date % 1000);
date /= 1000;
sec = (int)(date % 60);
date /= 60;
min = (int)(date % 60);
date /= 60;
hr = (int)(date % 24);
date /= 24;
day = (int)(date % 31) + 1;
date /= 31;
mnth = (int)(date % 12) + 1;
date /= 12;
yr = (int)(date - 9999);
if (yr < -9999 || yr > 9999)
goto Error;
return;
Error:
throw new XmlException(SR.SqlTypes_ArithOverflow, (string?)null);
}
private static void BreakDownXsdDate(long val, out int yr, out int mnth, out int day, out bool negTimeZone, out int hr, out int min)
{
if (val < 0)
goto Error;
val /= 4; // trim indicator bits
int totalMin = (int)(val % (29 * 60)) - 60 * 14;
long totalDays = val / (29 * 60);
if (negTimeZone = (totalMin < 0))
totalMin = -totalMin;
min = totalMin % 60;
hr = totalMin / 60;
day = (int)(totalDays % 31) + 1;
totalDays /= 31;
mnth = (int)(totalDays % 12) + 1;
yr = (int)(totalDays / 12) - 9999;
if (yr < -9999 || yr > 9999)
goto Error;
return;
Error:
throw new XmlException(SR.SqlTypes_ArithOverflow, (string?)null);
}
private static void BreakDownXsdTime(long val, out int hr, out int min, out int sec, out int ms)
{
if (val < 0)
goto Error;
val /= 4; // trim indicator bits
ms = (int)(val % 1000);
val /= 1000;
sec = (int)(val % 60);
val /= 60;
min = (int)(val % 60);
hr = (int)(val / 60);
if (0 > hr || hr > 23)
goto Error;
return;
Error:
throw new XmlException(SR.SqlTypes_ArithOverflow, (string?)null);
}
public static string XsdDateTimeToString(long val)
{
int yr;
int mnth;
int day;
int hr;
int min;
int sec;
int ms;
BreakDownXsdDateTime(val, out yr, out mnth, out day, out hr, out min, out sec, out ms);
StringBuilder sb = new StringBuilder(20);
WriteDate(sb, yr, mnth, day);
sb.Append('T');
WriteTime(sb, hr, min, sec, ms);
sb.Append('Z');
return sb.ToString();
}
public static DateTime XsdDateTimeToDateTime(long val)
{
int yr;
int mnth;
int day;
int hr;
int min;
int sec;
int ms;
BreakDownXsdDateTime(val, out yr, out mnth, out day, out hr, out min, out sec, out ms);
return new DateTime(yr, mnth, day, hr, min, sec, ms, DateTimeKind.Utc);
}
public static string XsdDateToString(long val)
{
int yr;
int mnth;
int day;
int hr;
int min;
bool negTimeZ;
BreakDownXsdDate(val, out yr, out mnth, out day, out negTimeZ, out hr, out min);
StringBuilder sb = new StringBuilder(20);
WriteDate(sb, yr, mnth, day);
WriteTimeZone(sb, negTimeZ, hr, min);
return sb.ToString();
}
public static DateTime XsdDateToDateTime(long val)
{
int yr;
int mnth;
int day;
int hr;
int min;
bool negTimeZ;
BreakDownXsdDate(val, out yr, out mnth, out day, out negTimeZ, out hr, out min);
DateTime d = new DateTime(yr, mnth, day, 0, 0, 0, DateTimeKind.Utc);
// adjust for timezone
int adj = (negTimeZ ? -1 : 1) * ((hr * 60) + min);
return TimeZoneInfo.ConvertTime(d.AddMinutes(adj), TimeZoneInfo.Local);
}
public static string XsdTimeToString(long val)
{
int hr;
int min;
int sec;
int ms;
BreakDownXsdTime(val, out hr, out min, out sec, out ms);
StringBuilder sb = new StringBuilder(16);
WriteTime(sb, hr, min, sec, ms);
sb.Append('Z');
return sb.ToString();
}
public static DateTime XsdTimeToDateTime(long val)
{
int hr;
int min;
int sec;
int ms;
BreakDownXsdTime(val, out hr, out min, out sec, out ms);
return new DateTime(1, 1, 1, hr, min, sec, ms, DateTimeKind.Utc);
}
public static string SqlDateTimeToString(int dateticks, uint timeticks)
{
DateTime dateTime = SqlDateTimeToDateTime(dateticks, timeticks);
string format = (dateTime.Millisecond != 0) ? "yyyy/MM/dd\\THH:mm:ss.ffff" : "yyyy/MM/dd\\THH:mm:ss";
return dateTime.ToString(format, CultureInfo.InvariantCulture);
}
public static DateTime SqlDateTimeToDateTime(int dateticks, uint timeticks)
{
DateTime SQLBaseDate = new DateTime(1900, 1, 1);
//long millisecond = (long)(((ulong)timeticks * 20 + (ulong)3) / (ulong)6);
long millisecond = (long)(timeticks / s_SQLTicksPerMillisecond + 0.5);
return SQLBaseDate.Add(new TimeSpan(dateticks * TimeSpan.TicksPerDay +
millisecond * TimeSpan.TicksPerMillisecond));
}
// Number of (100ns) ticks per time unit
private const double s_SQLTicksPerMillisecond = 0.3;
internal const int SQLTicksPerSecond = 300;
internal const int SQLTicksPerMinute = SQLTicksPerSecond * 60;
internal const int SQLTicksPerHour = SQLTicksPerMinute * 60;
public static string SqlSmallDateTimeToString(short dateticks, ushort timeticks)
{
DateTime dateTime = SqlSmallDateTimeToDateTime(dateticks, timeticks);
return dateTime.ToString("yyyy/MM/dd\\THH:mm:ss", CultureInfo.InvariantCulture);
}
public static DateTime SqlSmallDateTimeToDateTime(short dateticks, ushort timeticks)
{
return SqlDateTimeToDateTime((int)dateticks, (uint)(timeticks * SQLTicksPerMinute));
}
// Conversions of the Katmai date & time types to DateTime
public static DateTime XsdKatmaiDateToDateTime(byte[] data, int offset)
{
// Katmai SQL type "DATE"
long dateTicks = GetKatmaiDateTicks(data, ref offset);
DateTime dt = new DateTime(dateTicks);
return dt;
}
public static DateTime XsdKatmaiDateTimeToDateTime(byte[] data, int offset)
{
// Katmai SQL type "DATETIME2"
long timeTicks = GetKatmaiTimeTicks(data, ref offset);
long dateTicks = GetKatmaiDateTicks(data, ref offset);
DateTime dt = new DateTime(dateTicks + timeTicks);
return dt;
}
public static DateTime XsdKatmaiTimeToDateTime(byte[] data, int offset)
{
// TIME without zone is stored as DATETIME2
return XsdKatmaiDateTimeToDateTime(data, offset);
}
public static DateTime XsdKatmaiDateOffsetToDateTime(byte[] data, int offset)
{
// read the timezoned value into DateTimeOffset and then convert to local time
return XsdKatmaiDateOffsetToDateTimeOffset(data, offset).LocalDateTime;
}
public static DateTime XsdKatmaiDateTimeOffsetToDateTime(byte[] data, int offset)
{
// read the timezoned value into DateTimeOffset and then convert to local time
return XsdKatmaiDateTimeOffsetToDateTimeOffset(data, offset).LocalDateTime;
}
public static DateTime XsdKatmaiTimeOffsetToDateTime(byte[] data, int offset)
{
// read the timezoned value into DateTimeOffset and then convert to local time
return XsdKatmaiTimeOffsetToDateTimeOffset(data, offset).LocalDateTime;
}
public static DateTimeOffset XsdKatmaiDateOffsetToDateTimeOffset(byte[] data, int offset)
{
// DATE with zone is stored as DATETIMEOFFSET
return XsdKatmaiDateTimeOffsetToDateTimeOffset(data, offset);
}
public static DateTimeOffset XsdKatmaiDateTimeOffsetToDateTimeOffset(byte[] data, int offset)
{
// Katmai SQL type "DATETIMEOFFSET"
long timeTicks = GetKatmaiTimeTicks(data, ref offset);
long dateTicks = GetKatmaiDateTicks(data, ref offset);
long zoneTicks = GetKatmaiTimeZoneTicks(data, offset);
// The DATETIMEOFFSET values are serialized in UTC, but DateTimeOffset takes adjusted time -> we need to add zoneTicks
DateTimeOffset dto = new DateTimeOffset(dateTicks + timeTicks + zoneTicks, new TimeSpan(zoneTicks));
return dto;
}
public static DateTimeOffset XsdKatmaiTimeOffsetToDateTimeOffset(byte[] data, int offset)
{
// TIME with zone is stored as DATETIMEOFFSET
return XsdKatmaiDateTimeOffsetToDateTimeOffset(data, offset);
}
// Conversions of the Katmai date & time types to string
public static string XsdKatmaiDateToString(byte[] data, int offset)
{
DateTime dt = XsdKatmaiDateToDateTime(data, offset);
StringBuilder sb = new StringBuilder(10);
WriteDate(sb, dt.Year, dt.Month, dt.Day);
return sb.ToString();
}
public static string XsdKatmaiDateTimeToString(byte[] data, int offset)
{
DateTime dt = XsdKatmaiDateTimeToDateTime(data, offset);
StringBuilder sb = new StringBuilder(33);
WriteDate(sb, dt.Year, dt.Month, dt.Day);
sb.Append('T');
WriteTimeFullPrecision(sb, dt.Hour, dt.Minute, dt.Second, GetFractions(dt));
return sb.ToString();
}
public static string XsdKatmaiTimeToString(byte[] data, int offset)
{
DateTime dt = XsdKatmaiTimeToDateTime(data, offset);
StringBuilder sb = new StringBuilder(16);
WriteTimeFullPrecision(sb, dt.Hour, dt.Minute, dt.Second, GetFractions(dt));
return sb.ToString();
}
public static string XsdKatmaiDateOffsetToString(byte[] data, int offset)
{
DateTimeOffset dto = XsdKatmaiDateOffsetToDateTimeOffset(data, offset);
StringBuilder sb = new StringBuilder(16);
WriteDate(sb, dto.Year, dto.Month, dto.Day);
WriteTimeZone(sb, dto.Offset);
return sb.ToString();
}
public static string XsdKatmaiDateTimeOffsetToString(byte[] data, int offset)
{
DateTimeOffset dto = XsdKatmaiDateTimeOffsetToDateTimeOffset(data, offset);
StringBuilder sb = new StringBuilder(39);
WriteDate(sb, dto.Year, dto.Month, dto.Day);
sb.Append('T');
WriteTimeFullPrecision(sb, dto.Hour, dto.Minute, dto.Second, GetFractions(dto));
WriteTimeZone(sb, dto.Offset);
return sb.ToString();
}
public static string XsdKatmaiTimeOffsetToString(byte[] data, int offset)
{
DateTimeOffset dto = XsdKatmaiTimeOffsetToDateTimeOffset(data, offset);
StringBuilder sb = new StringBuilder(22);
WriteTimeFullPrecision(sb, dto.Hour, dto.Minute, dto.Second, GetFractions(dto));
WriteTimeZone(sb, dto.Offset);
return sb.ToString();
}
// Helper methods for the Katmai date & time types
private static long GetKatmaiDateTicks(byte[] data, ref int pos)
{
int p = pos;
pos = p + 3;
return (data[p] | data[p + 1] << 8 | data[p + 2] << 16) * TimeSpan.TicksPerDay;
}
private static long GetKatmaiTimeTicks(byte[] data, ref int pos)
{
int p = pos;
byte scale = data[p];
long timeTicks;
p++;
if (scale <= 2)
{
timeTicks = data[p] | (data[p + 1] << 8) | (data[p + 2] << 16);
pos = p + 3;
}
else if (scale <= 4)
{
timeTicks = data[p] | (data[p + 1] << 8) | (data[p + 2] << 16);
timeTicks |= ((long)data[p + 3] << 24);
pos = p + 4;
}
else if (scale <= 7)
{
timeTicks = data[p] | (data[p + 1] << 8) | (data[p + 2] << 16);
timeTicks |= ((long)data[p + 3] << 24) | ((long)data[p + 4] << 32);
pos = p + 5;
}
else
{
throw new XmlException(SR.SqlTypes_ArithOverflow, (string?)null);
}
return timeTicks * KatmaiTimeScaleMultiplicator[scale];
}
private static long GetKatmaiTimeZoneTicks(byte[] data, int pos)
{
return (short)(data[pos] | data[pos + 1] << 8) * TimeSpan.TicksPerMinute;
}
private static int GetFractions(DateTime dt)
{
return (int)(dt.Ticks - new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second).Ticks);
}
private static int GetFractions(DateTimeOffset dt)
{
return (int)(dt.Ticks - new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second).Ticks);
}
}
}
|