using System.Globalization; namespace decePubClient.Components.Nerd; /// One line of a NerdTip; a row without a value is not shown. public sealed record NerdRow(string Label, string Value) { public static NerdRow Of(string label, object value) => new(label, value switch { null => null, DateTime time => Time(time), double number => number.ToString("0.###", CultureInfo.InvariantCulture), bool flag => flag ? "yes" : "no", IFormattable formattable => formattable.ToString(null, CultureInfo.InvariantCulture), _ => value.ToString() }); /// A UTC time to the millisecond, as PrivaPub records it. public static string Time(DateTime time) => DateTime.SpecifyKind(time, time.Kind == DateTimeKind.Unspecified ? DateTimeKind.Utc : time.Kind).ToUniversalTime() .ToString("yyyy-MM-dd HH:mm:ss.fff'Z'", CultureInfo.InvariantCulture); /// A duration the way a person reads it: 850 ms, 12.4 s, 3 min 5 s, 2 h 10 min, 4 d 3 h. public static string Duration(TimeSpan span) { var sign = span < TimeSpan.Zero ? "-" : string.Empty; span = span.Duration(); return sign + (span.TotalSeconds switch { < 1 => $"{span.TotalMilliseconds:0} ms", < 60 => $"{span.TotalSeconds.ToString("0.0", CultureInfo.InvariantCulture)} s", < 3600 => $"{span.Minutes} min {span.Seconds} s", < 86400 => $"{(int)span.TotalHours} h {span.Minutes} min", _ => $"{(int)span.TotalDays} d {span.Hours} h" }); } /// A byte count: 912 B, 14.2 KiB, 3.1 MiB. public static string Bytes(long? bytes) => bytes switch { null => null, < 1024 => $"{bytes} B", < 1024 * 1024 => $"{(bytes.Value / 1024d).ToString("0.0", CultureInfo.InvariantCulture)} KiB", _ => $"{(bytes.Value / 1048576d).ToString("0.0", CultureInfo.InvariantCulture)} MiB" }; /// The great-circle distance between two points in kilometres (haversine, Earth radius 6371 km). public static double Kilometres(double latitude1, double longitude1, double latitude2, double longitude2) { const double radius = 6371; double Radians(double degrees) => degrees * Math.PI / 180; var dLatitude = Radians(latitude2 - latitude1); var dLongitude = Radians(longitude2 - longitude1); var a = Math.Pow(Math.Sin(dLatitude / 2), 2) + Math.Cos(Radians(latitude1)) * Math.Cos(Radians(latitude2)) * Math.Pow(Math.Sin(dLongitude / 2), 2); return 2 * radius * Math.Asin(Math.Min(1, Math.Sqrt(a))); } }