/* Copyright 2010-present MongoDB Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Reflection; using System.Text; using System.Text.RegularExpressions; namespace MongoDB.Bson { /// /// A static class containing BSON utility methods. /// public static class BsonUtils { // public static methods /// /// Gets a friendly class name suitable for use in error messages. /// /// The type. /// A friendly class name. public static string GetFriendlyTypeName(Type type) { var typeInfo = type.GetTypeInfo(); if (!typeInfo.IsGenericType) { return type.Name; } var sb = new StringBuilder(); sb.AppendFormat("{0}<", Regex.Replace(type.Name, @"\`\d+$", "")); foreach (var typeParameter in typeInfo.GetGenericArguments()) { sb.AppendFormat("{0}, ", GetFriendlyTypeName(typeParameter)); } sb.Remove(sb.Length - 2, 2); sb.Append(">"); return sb.ToString(); } /// /// Parses a hex string into its equivalent byte array. /// /// The hex string to parse. /// The byte equivalent of the hex string. public static byte[] ParseHexString(string s) { if (s == null) { throw new ArgumentNullException(nameof(s)); } byte[] bytes; if (!TryParseHexString(s, out bytes)) { throw new FormatException("String should contain only hexadecimal digits."); } return bytes; } /// /// Converts from number of milliseconds since Unix epoch to DateTime. /// /// The number of milliseconds since Unix epoch. /// A DateTime. public static DateTime ToDateTimeFromMillisecondsSinceEpoch(long millisecondsSinceEpoch) { if (millisecondsSinceEpoch < BsonConstants.DateTimeMinValueMillisecondsSinceEpoch || millisecondsSinceEpoch > BsonConstants.DateTimeMaxValueMillisecondsSinceEpoch) { var message = string.Format( "The value {0} for the BsonDateTime MillisecondsSinceEpoch is outside the" + "range that can be converted to a .NET DateTime.", millisecondsSinceEpoch); throw new ArgumentOutOfRangeException("millisecondsSinceEpoch", message); } // MaxValue has to be handled specially to avoid rounding errors if (millisecondsSinceEpoch == BsonConstants.DateTimeMaxValueMillisecondsSinceEpoch) { return DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc); } else { return BsonConstants.UnixEpoch.AddTicks(millisecondsSinceEpoch * 10000); } } /// /// Converts a value to a hex character. /// /// The value (assumed to be between 0 and 15). /// The hex character. public static char ToHexChar(int value) { return (char)(value + (value < 10 ? '0' : 'a' - 10)); } /// /// Converts a byte array to a hex string. /// /// The byte array. /// A hex string. public static string ToHexString(byte[] bytes) { if (bytes == null) { throw new ArgumentNullException("bytes"); } var length = bytes.Length; var c = new char[length * 2]; for (int i = 0, j = 0; i < length; i++) { var b = bytes[i]; c[j++] = ToHexChar(b >> 4); c[j++] = ToHexChar(b & 0x0f); } return new string(c); } /// /// Converts a DateTime to local time (with special handling for MinValue and MaxValue). /// /// A DateTime. /// The DateTime in local time. public static DateTime ToLocalTime(DateTime dateTime) { if (dateTime == DateTime.MinValue) { return DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Local); } else if (dateTime == DateTime.MaxValue) { return DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Local); } else { return dateTime.ToLocalTime(); } } /// /// Converts a DateTime to number of milliseconds since Unix epoch. /// /// A DateTime. /// Number of milliseconds since Unix epoch. public static long ToMillisecondsSinceEpoch(DateTime dateTime) { var utcDateTime = ToUniversalTime(dateTime); return (utcDateTime - BsonConstants.UnixEpoch).Ticks / 10000; } /// /// Converts a DateTime to number of seconds since Unix epoch. /// /// A DateTime. /// Number of seconds since Unix epoch. public static long ToSecondsSinceEpoch(DateTime dateTime) { var utcDateTime = ToUniversalTime(dateTime); return (utcDateTime - BsonConstants.UnixEpoch).Ticks / TimeSpan.TicksPerSecond; } /// /// Converts a DateTime to UTC (with special handling for MinValue and MaxValue). /// /// A DateTime. /// The DateTime in UTC. public static DateTime ToUniversalTime(DateTime dateTime) { if (dateTime == DateTime.MinValue) { return DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc); } else if (dateTime == DateTime.MaxValue) { return DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc); } else { return dateTime.ToUniversalTime(); } } /// /// Tries to parse a hex string to a byte array. /// /// The hex string. /// A byte array. /// True if the hex string was successfully parsed. public static bool TryParseHexString(string s, out byte[] bytes) { bytes = null; if (s == null) { return false; } var buffer = new byte[(s.Length + 1) / 2]; var i = 0; var j = 0; if ((s.Length % 2) == 1) { // if s has an odd length assume an implied leading "0" int y; if (!TryParseHexChar(s[i++], out y)) { return false; } buffer[j++] = (byte)y; } while (i < s.Length) { int x, y; if (!TryParseHexChar(s[i++], out x)) { return false; } if (!TryParseHexChar(s[i++], out y)) { return false; } buffer[j++] = (byte)((x << 4) | y); } bytes = buffer; return true; } // private static methods private static bool TryParseHexChar(char c, out int value) { if (c >= '0' && c <= '9') { value = c - '0'; return true; } if (c >= 'a' && c <= 'f') { value = 10 + (c - 'a'); return true; } if (c >= 'A' && c <= 'F') { value = 10 + (c - 'A'); return true; } value = 0; return false; } } }