I would like to write a converter from milliseconds to the highest possible time value before reaching a 0,x value.
Let me clarify this with examples.
Let's assume you have 1500ms this should result in 1,5secs, because its the highest possible digit value not resulting in 0,x.
So different examples would be
10ms = 10,0ms
100ms = 100,0ms
1000ms = 1,0sec
10000ms = 10,0sec
100000ms = 1,6min
1000000ms = 16,0min
10000000ms = 2,7hours
(The method should more or less be endless, so from hours to days, to weeks, to months, to years, to decades and so on...)
Is there a .net method for this?
Something like the following
public static string ConversionMethod(UInt64 ms)
{
// change output format as needed
string format = "######.###";
var cutoffs = new List<UInt64>() {
1000, // second
60000, // minute
3600000, // hour
86400000, // day
604800000, // week = day * 7
2592000000, // month = day * 30
31536000000, // year = day * 365
315360000000, // decade = year * 10
3153600000000, // century = decade * 10 (100 years)
31536000000000, // millenia = century * 10 (1000 years)
31536000000000000 // megayear = year * 100000
// 18446744073709551615 // UInt64 MaxValue
// 31536000000000000000 // gigayear = year * 100000000
};
var postfix = new List<String>() {
"second",
"minute",
"hour",
"day",
"week",
"month",
"year",
"decade",
"century",
"millenia",
"megayear"
};
// The above are listed from smallest to largest for easy reading,
// but the comparisons need to be made from largest to
// smallest (in the loop below)
cutoffs.Reverse();
postfix.Reverse();
int count = 0;
foreach (var cutoff in cutoffs)
{
if (ms > cutoff)
{
return ((decimal)((decimal)ms / (decimal)cutoff)).ToString(format) + " " + postfix[count];
}
count++;
}
return ms + " ms";
}
Conversion for the fraction is a bit dirty, might want to clean that up. Also, you'll have to decide how you want to handle leap years (and leap seconds), etc.
While not the final solution, maybe TimeSpan can help you achieve what you are looking for.
It is to be noted however, TimeSpan supports only up to TotalDays.
var timespan = TimeSpan.FromMilliseconds(1500);
var seconds = timespan.TotalSeconds; // equals: 1.5
It seems the TimeSpan class is the closest thing that meets your need, but clearly it's not exactly what you want. My take on it would look something like this:
public static string ScientificNotationTimespan(int milliseconds)
{
var timeSpan = new TimeSpan(0, 0, 0, 0, milliseconds);
var totalDays = timeSpan.TotalDays;
if (totalDays < 7)
{
if (timeSpan.TotalDays > 1) return timeSpan.TotalDays.ToString() + " days";
if (timeSpan.TotalHours > 1) return timeSpan.TotalHours.ToString() + " hours";
if (timeSpan.TotalMinutes > 1) return timeSpan.TotalMinutes.ToString() + " minutes";
if (timeSpan.TotalSeconds > 1) return timeSpan.TotalSeconds.ToString() + " seconds";
return milliseconds.ToString() + "milliseconds";
}
var weeks = totalDays / 7;
//How long is a month? 28, 29, 30 or 31 days?
var years = totalDays / 365;
if (years < 1) return weeks.ToString() + " weeks";
var decades = years / 10;
if (decades < 1) return years.ToString() + " years";
var centuries = decades / 10;
if (centuries < 1) return decades.ToString() + " decades";
var millenia = centuries / 10;
if (millenia < 1) return centuries.ToString() + " centuries";
return millenia.ToString() + " millenia";
}
Here is solution for years, months using DateTime and Gregorian calendar (meaning leap years, calendar months). Then it uses the TimeSpan solution as already submitted.
static string ToMostNonZeroTime(long ms) {
const int hundretsNanosecondsInMillisecond = 10000;
long ticks = (long)ms * hundretsNanosecondsInMillisecond;
var dt = new DateTime(ticks);
if((dt.Year - 1) > 0) { // starts with 1
double daysToYear = (dt.DayOfYear - 1) * 1.0 / (DateTime.IsLeapYear(dt.Year) ? 366 : 365);
daysToYear += dt.Year - 1;
return $"{daysToYear:0.0} years";
}
if((dt.Month - 1) > 0) {
double daysToMonth = (dt.Day - 1) * 1.0 / DateTime.DaysInMonth(dt.Year, dt.Month);
daysToMonth += dt.Day - 1;
return $"{daysToMonth:0.0} months";
}
// can use TimeSpan then:
var ts = TimeSpan.FromMilliseconds(ms);
if(ts.TotalDays >= 1)
return $"{ts.TotalDays:0.0} days";
if(ts.TotalHours >= 1)
return $"{ts.TotalHours:0.0} hours";
if(ts.TotalMinutes >= 1)
return $"{ts.TotalMinutes:0.0} minutes";
if(ts.TotalSeconds >= 1)
return $"{ts.TotalSeconds:0.0} seconds";
return $"{ms} milliseconds";
}
It prints
100ms: 100 milliseconds
1000ms: 1.0 seconds
10000ms: 10.0 seconds
100000ms: 1.7 minutes
1000000ms: 16.7 minutes
10000000ms: 2.8 hours
100000000ms: 1.2 days
1000000000ms: 11.6 days
20000000000ms: 19.6 months
200000000000ms: 6.3 years
Have a look at https://ideone.com/QZHOM4
I'm trying to convert a Julian Date which includes hours minutes and seconds to a DateTime in C#.
This is the number: 2457285.7795969
I can calculate the DateTime excluding the hours and the minutes with this function.
public static DateTime FromJulian(long julianDate)
{
long L = julianDate + 68569;
long N = (long)((4 * L) / 146097);
L = L - ((long)((146097 * N + 3) / 4));
long I = (long)((4000 * (L + 1) / 1461001));
L = L - (long)((1461 * I) / 4) + 31;
long J = (long)((80 * L) / 2447);
int Day = (int)(L - (long)((2447 * J) / 80));
L = (long)(J / 11);
int Month = (int)(J + 2 - 12 * L);
int Year = (int)(100 * (N - 49) + I + L);
return new DateTime(Year, Month, Day);
}
It should be as simple as:
public static DateTime FromJulian(double julianDate)
{
return new DateTime(
(long)((julianDate - 1721425.5) * TimeSpan.TicksPerDay),
DateTimeKind.Utc);
}
As you can see, 1721425.5 is the so-called Gregorian epoch, i.e. the value the Julian date had at the beginning of the proleptic Gregorian calendar, at 0001 January 1, 00:00:00.0000000, where the .NET DateTime has its origin.
EDIT: If you want to make sure your method throws an exception on "extreme" inputs instead of returning an invalid value, do this:
public static DateTime FromJulian(double julianDate)
{
return new DateTime(
checked((long)((julianDate - 1721425.5) * TimeSpan.TicksPerDay)),
DateTimeKind.Utc);
}
Note that we do the multiplication with the double operator *(double, double) overload (built-in in C#). This gives an error as little as possible. The conversion from double to long will throw in checked context if the double is outside the range of long. If that conversion goes well, the DateTime constructor may throw if the value of the long is out of range for a .NET DateTime.
NEW EDIT: Inspired by another thread (Convert DateTime to Julian Date in C# (ToOADate Safe?)) you can also work out a very simple solution using DateTime.FromOADate. However, see another Stack Overflow post by myself on precision short-comings of the FromOADate method.
Is this what you are looking for: Convert Julian Date with Time (H/m/s) to Date Time in C#?
Applying that answer, converting your value of 2457285.7795969 results in 9/19/2015 11:42:37 PM.
Before Ladi answered with what I was looking for....
double L = DateTime.Now.ToOADate() + 2415018.5 + 68569;
double HMS = L-(int)L-0.5;
int Hours = (int)(24*HMS);
HMS=HMS - (double)(Hours/24.0);
int Mins = (int)(24*60*HMS);
HMS=HMS - (double)(Mins/(24.0*60));
int Secs = (int)(24*60*60*HMS);
long N = (long)((4 * L) / 146097);
L = L - ((long)((146097 * N + 3) / 4));
long I = (long)((4000 * (L + 1) / 1461001));
L = L - (long)((1461 * I) / 4) + 31;
long J = (long)((80 * L) / 2447);
int Day = (int)(L - (long)((2447 * J) / 80));
L = (long)(J / 11);
int Month = (int)(J + 2 - 12 * L);
int Year = (int)(100 * (N - 49) + I + L);
DateTime test = new DateTime(Year, Month, Day);
Console.WriteLine("Hours-"+Hours);
Console.WriteLine("Mins-" + Mins);
Console.WriteLine("Secs-"+ Secs);
Console.WriteLine(test);
Console.WriteLine(DateTime.Now.ToString());
What is the recommended way of formatting TimeSpan objects into a string with a custom format?
Please note: this answer is for .Net 4.0 and above. If you want to format a TimeSpan in .Net 3.5 or below please see JohannesH's answer.
Custom TimeSpan format strings were introduced in .Net 4.0. You can find a full reference of available format specifiers at the MSDN Custom TimeSpan Format Strings page.
Here's an example timespan format string:
string.Format("{0:hh\\:mm\\:ss}", myTimeSpan); //example output 15:36:15
(UPDATE) and here is an example using C# 6 string interpolation:
$"{myTimeSpan:hh\\:mm\\:ss}"; //example output 15:36:15
You need to escape the ":" character with a "\" (which itself must be escaped unless you're using a verbatim string).
This excerpt from the MSDN Custom TimeSpan Format Strings page explains about escaping the ":" and "." characters in a format string:
The custom TimeSpan format specifiers do not include placeholder separator symbols, such as the symbols that separate days from hours, hours from minutes, or seconds from fractional seconds. Instead, these symbols must be included in the custom format string as string literals. For example, "dd.hh:mm" defines a period (.) as the separator between days and hours, and a colon (:) as the separator between hours and minutes.
For .NET 3.5 and lower you could use:
string.Format ("{0:00}:{1:00}:{2:00}",
(int)myTimeSpan.TotalHours,
myTimeSpan.Minutes,
myTimeSpan.Seconds);
Code taken from a Jon Skeet answer on bytes
For .NET 4.0 and above, see DoctaJonez answer.
One way is to create a DateTime object and use it for formatting:
new DateTime(myTimeSpan.Ticks).ToString(myCustomFormat)
// or using String.Format:
String.Format("{0:HHmmss}", new DateTime(myTimeSpan.Ticks))
This is the way I know. I hope someone can suggest a better way.
Simple. Use TimeSpan.ToString with c, g or G. More information at MSDN
I would go with
myTimeSpan.ToString("hh\\:mm\\:ss");
Personally, I like this approach:
TimeSpan ts = ...;
string.Format("{0:%d}d {0:%h}h {0:%m}m {0:%s}s", ts);
You can make this as custom as you like with no problems:
string.Format("{0:%d}days {0:%h}hours {0:%m}min {0:%s}sec", ts);
string.Format("{0:%d}d {0:%h}h {0:%m}' {0:%s}''", ts);
Dim duration As New TimeSpan(1, 12, 23, 62)
DEBUG.WriteLine("Time of Travel: " + duration.ToString("dd\.hh\:mm\:ss"))
It works for Framework 4
http://msdn.microsoft.com/en-us/library/ee372287.aspx
This is awesome one:
string.Format("{0:00}:{1:00}:{2:00}",
(int)myTimeSpan.TotalHours,
myTimeSpan.Minutes,
myTimeSpan.Seconds);
You can also go with:
Dim ts As New TimeSpan(35, 21, 59, 59) '(11, 22, 30, 30) '
Dim TimeStr1 As String = String.Format("{0:c}", ts)
Dim TimeStr2 As String = New Date(ts.Ticks).ToString("dd.HH:mm:ss")
EDIT:
You can also look at Strings.Format.
Dim ts As New TimeSpan(23, 30, 59)
Dim str As String = Strings.Format(New DateTime(ts.Ticks), "H:mm:ss")
if (timeSpan.TotalDays < 1)
return timeSpan.ToString(#"hh\:mm\:ss");
return timeSpan.TotalDays < 2
? timeSpan.ToString(#"d\ \d\a\y\ hh\:mm\:ss")
: timeSpan.ToString(#"d\ \d\a\y\s\ hh\:mm\:ss");
All literal characters must be escaped.
This is the approach I used my self with conditional formatting. and I post it here because I think this is clean way.
$"{time.Days:#0:;;\\}{time.Hours:#0:;;\\}{time.Minutes:00:}{time.Seconds:00}"
example of outputs:
00:00 (minimum)
1:43:04 (when we have hours)
15:03:01 (when hours are more than 1 digit)
2:4:22:04 (when we have days.)
The formatting is easy. time.Days:#0:;;\\ the format before ;; is for when value is positive. negative values are ignored. and for zero values we have;;\\ in order to hide it in formatted string. note that the escaped backslash is necessary otherwise it will not format correctly.
Here is my extension method:
public static string ToFormattedString(this TimeSpan ts)
{
const string separator = ", ";
if (ts.TotalMilliseconds < 1) { return "No time"; }
return string.Join(separator, new string[]
{
ts.Days > 0 ? ts.Days + (ts.Days > 1 ? " days" : " day") : null,
ts.Hours > 0 ? ts.Hours + (ts.Hours > 1 ? " hours" : " hour") : null,
ts.Minutes > 0 ? ts.Minutes + (ts.Minutes > 1 ? " minutes" : " minute") : null,
ts.Seconds > 0 ? ts.Seconds + (ts.Seconds > 1 ? " seconds" : " second") : null,
ts.Milliseconds > 0 ? ts.Milliseconds + (ts.Milliseconds > 1 ? " milliseconds" : " millisecond") : null,
}.Where(t => t != null));
}
Example call:
string time = new TimeSpan(3, 14, 15, 0, 65).ToFormattedString();
Output:
3 days, 14 hours, 15 minutes, 65 milliseconds
I used the code below. It is long, but still it is one expression, and produces very friendly output, as it does not outputs days, hours, minutes, or seconds if they have value of zero.
In the sample it produces output: "4 days 1 hour 3 seconds".
TimeSpan sp = new TimeSpan(4,1,0,3);
string.Format("{0}{1}{2}{3}",
sp.Days > 0 ? ( sp.Days > 1 ? sp.ToString(#"d\ \d\a\y\s\ "): sp.ToString(#"d\ \d\a\y\ ")):string.Empty,
sp.Hours > 0 ? (sp.Hours > 1 ? sp.ToString(#"h\ \h\o\u\r\s\ ") : sp.ToString(#"h\ \h\o\u\r\ ")):string.Empty,
sp.Minutes > 0 ? (sp.Minutes > 1 ? sp.ToString(#"m\ \m\i\n\u\t\e\s\ ") :sp.ToString(#"m\ \m\i\n\u\t\e\ ")):string.Empty,
sp.Seconds > 0 ? (sp.Seconds > 1 ? sp.ToString(#"s\ \s\e\c\o\n\d\s"): sp.ToString(#"s\ \s\e\c\o\n\d\s")):string.Empty);
I use this method. I'm Belgian and speak dutch so plural of hours and minutes is not just adding 's' to the end but almost a different word than singular.
It may seem long but it is very readable I think:
public static string SpanToReadableTime(TimeSpan span)
{
string[] values = new string[4]; //4 slots: days, hours, minutes, seconds
StringBuilder readableTime = new StringBuilder();
if (span.Days > 0)
{
if (span.Days == 1)
values[0] = span.Days.ToString() + " dag"; //day
else
values[0] = span.Days.ToString() + " dagen"; //days
readableTime.Append(values[0]);
readableTime.Append(", ");
}
else
values[0] = String.Empty;
if (span.Hours > 0)
{
if (span.Hours == 1)
values[1] = span.Hours.ToString() + " uur"; //hour
else
values[1] = span.Hours.ToString() + " uren"; //hours
readableTime.Append(values[1]);
readableTime.Append(", ");
}
else
values[1] = string.Empty;
if (span.Minutes > 0)
{
if (span.Minutes == 1)
values[2] = span.Minutes.ToString() + " minuut"; //minute
else
values[2] = span.Minutes.ToString() + " minuten"; //minutes
readableTime.Append(values[2]);
readableTime.Append(", ");
}
else
values[2] = string.Empty;
if (span.Seconds > 0)
{
if (span.Seconds == 1)
values[3] = span.Seconds.ToString() + " seconde"; //second
else
values[3] = span.Seconds.ToString() + " seconden"; //seconds
readableTime.Append(values[3]);
}
else
values[3] = string.Empty;
return readableTime.ToString();
}//end SpanToReadableTime
This is a pain in VS 2010, here's my workaround solution.
public string DurationString
{
get
{
if (this.Duration.TotalHours < 24)
return new DateTime(this.Duration.Ticks).ToString("HH:mm");
else //If duration is more than 24 hours
{
double totalminutes = this.Duration.TotalMinutes;
double hours = totalminutes / 60;
double minutes = this.Duration.TotalMinutes - (Math.Floor(hours) * 60);
string result = string.Format("{0}:{1}", Math.Floor(hours).ToString("00"), Math.Floor(minutes).ToString("00"));
return result;
}
}
}
The Substring method works perfectly when you only want the Hours:Minutes:Seconds. It's simple, clean code and easy to understand.
var yourTimeSpan = DateTime.Now - DateTime.Now.AddMinutes(-2);
var formatted = yourTimeSpan.ToString().Substring(0,8);// 00:00:00
Console.WriteLine(formatted);
No one has shown approach with decimal format specifier which is my favorite one, especially when used with string interpolation - https://learn.microsoft.com/en-us/dotnet/standard/base-types/standard-numeric-format-strings?redirectedfrom=MSDN#decimal-format-specifier-d
For example:
$"{time.Hours:D2}:{time.Minutes:D2}:{time.Seconds:D2}.{time.Milliseconds:D3}"
// Sample output: 00:00:09.200
You can of course wrap it up in some helper method.
Here is my version. It shows only as much as necessary, handles pluralization, negatives, and I tried to make it lightweight.
Output Examples
0 seconds
1.404 seconds
1 hour, 14.4 seconds
14 hours, 57 minutes, 22.473 seconds
1 day, 14 hours, 57 minutes, 22.475 seconds
Code
public static class TimeSpanExtensions
{
public static string ToReadableString(this TimeSpan timeSpan)
{
int days = (int)(timeSpan.Ticks / TimeSpan.TicksPerDay);
long subDayTicks = timeSpan.Ticks % TimeSpan.TicksPerDay;
bool isNegative = false;
if (timeSpan.Ticks < 0L)
{
isNegative = true;
days = -days;
subDayTicks = -subDayTicks;
}
int hours = (int)((subDayTicks / TimeSpan.TicksPerHour) % 24L);
int minutes = (int)((subDayTicks / TimeSpan.TicksPerMinute) % 60L);
int seconds = (int)((subDayTicks / TimeSpan.TicksPerSecond) % 60L);
int subSecondTicks = (int)(subDayTicks % TimeSpan.TicksPerSecond);
double fractionalSeconds = (double)subSecondTicks / TimeSpan.TicksPerSecond;
var parts = new List<string>(4);
if (days > 0)
parts.Add(string.Format("{0} day{1}", days, days == 1 ? null : "s"));
if (hours > 0)
parts.Add(string.Format("{0} hour{1}", hours, hours == 1 ? null : "s"));
if (minutes > 0)
parts.Add(string.Format("{0} minute{1}", minutes, minutes == 1 ? null : "s"));
if (fractionalSeconds.Equals(0D))
{
switch (seconds)
{
case 0:
// Only write "0 seconds" if we haven't written anything at all.
if (parts.Count == 0)
parts.Add("0 seconds");
break;
case 1:
parts.Add("1 second");
break;
default:
parts.Add(seconds + " seconds");
break;
}
}
else
{
parts.Add(string.Format("{0}{1:.###} seconds", seconds, fractionalSeconds));
}
string resultString = string.Join(", ", parts);
return isNegative ? "(negative) " + resultString : resultString;
}
}
If you want the duration format similar to youtube, given the number of seconds
int[] duration = { 0, 4, 40, 59, 60, 61, 400, 4000, 40000, 400000 };
foreach (int d in duration)
{
Console.WriteLine("{0, 6} -> {1, 10}", d, d > 59 ? TimeSpan.FromSeconds(d).ToString().TrimStart("00:".ToCharArray()) : string.Format("0:{0:00}", d));
}
Output:
0 -> 0:00
4 -> 0:04
40 -> 0:40
59 -> 0:59
60 -> 1:00
61 -> 1:01
400 -> 6:40
4000 -> 1:06:40
40000 -> 11:06:40
400000 -> 4.15:06:40
I wanted to return a string such as "1 day 2 hours 3 minutes" and also take into account if for example days or minuttes are 0 and then not showing them. thanks to John Rasch for his answer which mine is barely an extension of
TimeSpan timeLeft = New Timespan(0, 70, 0);
String.Format("{0}{1}{2}{3}{4}{5}",
Math.Floor(timeLeft.TotalDays) == 0 ? "" :
Math.Floor(timeLeft.TotalDays).ToString() + " ",
Math.Floor(timeLeft.TotalDays) == 0 ? "" : Math.Floor(timeLeft.TotalDays) == 1 ? "day " : "days ",
timeLeft.Hours == 0 ? "" : timeLeft.Hours.ToString() + " ",
timeLeft.Hours == 0 ? "" : timeLeft.Hours == 1 ? "hour " : "hours ",
timeLeft.Minutes == 0 ? "" : timeLeft.Minutes.ToString() + " ",
timeLeft.Minutes == 0 ? "" : timeLeft.Minutes == 1 ? "minute " : "minutes ");
I would like to calculate the remaining minutes to the "next" half an hour or hour.
Say i get a start time string of 07:15, i want it to calculate the remaining minutes to the nearest half an hour (07:30).
That would be 15min.
Then i can also have an instance where the start time can be 07:45 and i want it to calculate the remaining minutes to the nearest hour (08:00).
That would also be 15min.
So any string less then 30min in a hour would calculate to the nearest half an hour (..:30) and any string over 30min would calculate to the nearest hour (..:00).
I don't want to do a bunch of if statements, because i get from time strings that can start from and minute in an hour.
This is what i do not want to do:
if (int.Parse(fromTimeString.Right(2)) < 30)
{
//Do Calculation
}
else
{
//Do Calculation
}
public static string Right(this String stringValue, int noOfCharacters)
{
string result = null;
if (stringValue.Length >= noOfCharacters)
{
result = stringValue.Substring(stringValue.Length - noOfCharacters, noOfCharacters);
}
else
{
result = "";
}
return result;
}
Is there not an easier way with linq or with the DateTime class
Use modulo operator % with 30. Your result will be equal to (60 - currentMinutes) % 30. About LINQ its used for collections so i can't realy see how it can be used in your case.
You can use this DateTime tick-round approach to get the timespan until next half hour:
var minutes = 30;
var now = DateTime.Now;
var ticksMin = TimeSpan.FromMinutes(minutes).Ticks;
DateTime rounded = new DateTime(((now.Ticks + (ticksMin/2)) / ticksMin) * ticksMin);
var diff=rounded-now;
var minUntilNext = diff.TotalMinutes > 0 ? diff.TotalMinutes : minutes + diff.TotalMinutes;
var minutesToNextHalfHour = (60 - yourDateTimeVariable.Minutes) % 30;
This should do it:
int remainingMinutes = (current.Minute >= 30)
? 60 - current.Minute
: 30 - current.Minute;
var hhmm = fromTimeString.Split(':');
var mins = int.Parse(hhmm[1]);
var remainingMins = (60 - mins) % 30;
var str = "7:16";
var datetime = DateTime.ParseExact(str, "h:mm", new CultureInfo("en-US"));
var minutesPastHalfHour = datetime.Minute % 30;
var minutesBeforeHalfHour = 30 - minutesPastHalfHour;
I would use modulo + TimeSpan.TryParse:
public static int ComputeTime(string time)
{
TimeSpan ts;
if (TimeSpan.TryParse(time, out ts))
{
return (60 - ts.Minutes) % 30;
}
throw new ArgumentException("Time is not valid", "time");
}
private static void Main(string[] args)
{
string test1 = "7:27";
string test2 = "7:42";
Console.WriteLine(ComputeTime(test1));
Console.WriteLine(ComputeTime(test2));
Console.ReadLine();
}