String format with a negative timespan - c#

I have an issue with a string format with a timespan.
string.Format("{0:00}:{1:00}", Math.Floor(ts.TotalHours), ts.Minutes)
Which produce this result 12:08 but the problem is that it can go in minus then it look like this -01:-59 which is not correct it should look like this -01:59.
I have tried to use the Math.Abs, but it will just show a 0 even if the number is -56
What is the best way to handle this?

You can use Math.Abs:
string.Format("{0:00}:{1:00}", Math.Floor((decimal)ts.Hours), Math.Abs(ts.Minutes))
Examples:
TimeSpan ts = new TimeSpan(-1, -1, 0); // returns -01:01
ts = new TimeSpan(-1, 1, 0); // returns -00:59
ts = new TimeSpan(1, 1, 0); // returns 01:01

Related

TimeSpan calculation is different between TimeOnly and DateTime

I'm having a hard time understanding why this is happening or even if this should be happening. If I calculate the TimeSpan between two DateTime objects (same date, different times) and compare it to the same calculation using two TimeOnly objects I get different results.
var start = new DateTime(1, 1, 1, 14, 0, 0);
var end = new DateTime(1, 1, 1, 10, 0, 0);
Console.WriteLine(end - start); // Prints -4 hours
However...
var start = new TimeOnly(14, 0, 0);
var end = new TimeOnly(10, 0, 0);
Console.WriteLine(end - start); // Prints 20 hours???
Isn't the span between starting at 2pm and ending at 10am always a span of -4 hours? Interestingly enough if I take the second one and do Console.WriteLine(end.ToTimeSpan() - start.ToTimeSpan()); I end up with -4 hours.
This feels like an error on TimeOnly's part but I don't know. Here is a fiddle I did comparing results between NodaTime, System.DateTime, converting System.TimeOnly to TimeSpan, and System.TimeOnly.

Comparing times when over 24 hours

I have a TimeSpan field that adds up time spent on something. For example the time could be 33 hours, so the format is 33:56:00
I want to compare this to 10 hours to calculate how many over hours were done.
TimeSpan totalActualHours = new TimeSpan(0, 0, 0, 0, 0);
if (totalActualHours > TimeSpan.Parse(txtEstimateHrs.Text))
{
tmpOverHours = totalActualHours.Subtract(TimeSpan.Parse(txtEstimateHrs.Text));
}
But since totalActualHours is over 24 hours the format is coming out like 1.09:56:00 instead of 33:56:00. So txtEstimateHrs.Text is equal to 10 and I want to see if 33:56:00 is greater and if so then how many hours is it greater?
So the code is comparing if (1.09:56:00 > 10.00:00:00) so it never goes into the if statement.
The issue here is Timespan in converting the hours into days so 33 hours changes to 1 day and 9 hours, and the txtEstimateHrs.Text is an integar 10 and that changes to 10 days. I need both times to be in hours format and be able to compare them
You just need to properly construct the timespan object using the appropiate format. In your case, you can choose between
hour, min sec
day, hour, min, sec, millisec
Sample code:
Case 1
TimeSpan tmpOverHours;
TimeSpan totalActualHours = new TimeSpan(33, 56, 0);
TimeSpan hoursToCompare = new TimeSpan(int.Parse(txtEstimateHrs.Text), 0, 0);
if (totalActualHours > hoursToCompare)
{
tmpOverHours = totalActualHours.Subtract(hoursToCompare);
}
Case 2
TimeSpan tmpOverHours;
TimeSpan totalActualHours = new TimeSpan(0, 33, 56, 0, 0);
TimeSpan hoursToCompare = new TimeSpan(0, int.Parse(txtEstimateHrs.Text), 0, 0, 0);
if (totalActualHours > hoursToCompare)
{
tmpOverHours = totalActualHours.Subtract(hoursToCompare);
}
It seems you are having a parsing error when you are doing:
TimeSpan.Parse(txtEstimateHrs.Text)
if the text is "10" the parse method will interpret the value as days.
So you could change that code to something like:
TimeSpan.FromHours(int.Parse(txtEstimateHrs.Text))
Which will parse the number in the textbox into an int and use that to create a TimeSpan which correctly has the number of hours and not days.
Edit: On a side note, don't parse the text twice, better use a variable to hold the parsed TimeSpan and then use it.
I am not sure i understood your requirement but you can use the TimeSpan.Compare() method.
var t1 = new TimeSpan(33, 21, 12);
var t2 = new TimeSpan(10, 0, 0);
if (TimeSpan.Compare(t1, t2) > 0)
{
Console.WriteLine(t1.ToString() + " is longer");
}
Edit:
The above code will work fine if the Timespan objects can be created correctly. In case you are working with strings in the format of hh:mm:ss then you will need to split them and call the correct Timespan constructor. Something like below:
public static TimeSpan ConvertStringToTimeStamp(string s)
{
// add checks for input like >0, not null or empty
var split = s.Split(':');
TimeSpan ts;
switch (split.Length)
{
case 3:
ts = new TimeSpan(int.Parse(split[0]), // hours
int.Parse(split[1]), // minutes
int.Parse(split[2])); // seconds // seconds);
break;
case 2:
ts = new TimeSpan(int.Parse(split[0]), // hours
int.Parse(split[1]), // minutes
0); // 0 seconds
break;
case 1:
ts = new TimeSpan(int.Parse(split[0]), // hours
0, // 0 minutes
0); // 0 seconds
break;
default:
throw new Exception("Invalid Input");
}
return ts;
}

TimeSpan.ToString() return string like (d:hh:mm:ss)

TimeSpan Ts = new TimeSpan(5, 4, 3, 2);
return Ts.ToString("?");
What expression should I replace with a question mark to get this format:
5d:4h:3m:2s ?
TimeSpan timeSpan = new TimeSpan(5, 4, 3, 2);
string str = timeSpan.ToString(#"d\d\:h\h\:m\m\:s\s", System.Globalization.CultureInfo.InvariantCulture);
See Custom TimeSpan Format Strings on how to format TimeSpans.
Though note that negative TimeSpans cannot be distinguished from positive ones. They appear like they have been negated. Therefor -new TimeSpan(5,4,3,2) will still show as 5d:4h:3m:2s. If you want negative numbers to display, you should format your own numbers though the properties of TimeSpan.
You can accomplish this by using your current code
TimeSpan Ts = new TimeSpan(5, 4, 3, 2);
var RetValue = string.Format("{0}d:{1}h:{2}m:{3}s",
Ts.Days,
Ts.Hours,
Ts.Minutes,
Ts.Seconds);
yields this as a formatted result "5d:4h:0m:2s"
This works for me
"d'd:'h'h:'m'm:'s's'"
Found here http://msdn.microsoft.com/en-us/library/ee372287.aspx
Here's a TimeSpan Extension method that will hide empty large time parts.
public static string ToShortString(this TimeSpan Ts)
{
if(Ts.TotalDays > 1d)
return Ts.ToString("d'd:'h'h:'m'm:'s's'");
if(Ts.TotalHours > 1d)
return Ts.ToString("h'h:'m'm:'s's'");
if(Ts.TotalMinutes > 1d)
return Ts.ToString("m'm:'s's'");
if(Ts.TotalSeconds > 1d)
return Ts.ToString("s's'");
if(Ts.TotalMilliseconds > 1d)
return Ts.ToString("fffffff'ms'");
return Ts.ToString();
}

Formatting a TimeSpan to look like a time zone offset

How can I format a TimeSpan object to look like a time zone offset, like this:
+0700
or
-0600
I'm using GetUtcOffset to get an offset, and its working, but its returning a TimeSpan object.
If you're using .Net 4.0 or above, you can use the ToString method on timespan with the hh and mm specifier (not sure if it will display the + and - signs though):
TimeSpan span = new TimeSpan(7, 0, 0);
Console.WriteLine(span.ToString("hhmm"));
If not, you can just format the Hours and Minutes properties along with some conditional formatting to always display the + and - signs:
TimeSpan span = new TimeSpan(7, 0, 0);
Console.WriteLine("{0:+00;-00}{1:00}", span.Hours, span.Minutes);
Reference for TimeSpan format strings: http://msdn.microsoft.com/en-gb/library/ee372287.aspx
Reference for numeric format strings and conditional formatting of them: http://msdn.microsoft.com/en-us/library/0c899ak8.aspx
Try something like:
var timespan = new TimeSpan(-5,0,0); // EST
var offset = String.Format("{0}{1:00}{2:00}",(timespan.Hours >= 0 ? "+" : String.Empty),timespan.Hours,timespan.Minutes);
I add the + when the number is non-negative (for negative numbers a - should be output).
This code:
var timeSpan = new TimeSpan(2, 30, 0);
Console.WriteLine(new DateTimeOffset(2000, 1, 1, 1, 1, 1, timeSpan).ToString("zzz"));
Console.WriteLine(new DateTimeOffset(2000, 1, 1, 1, 1, 1, -timeSpan).ToString("zzz"));
outputs:
+02:30
-02:30
I think you could use this:
String.Format("{0:zzz}", ts);

Timespan between Now and Next Hour?

It is 8:30 and I am trying to find out how many seconds there are between now and the next whole hour (9:00). I think I just want to DateTime.Now.AddHours(1) but after I do that I think I need the "floor". How to get that value?
Thanks.
Just round the time of day in hours up to the next integral value:
var timeOfDay = DateTime.Now.TimeOfDay;
var nextFullHour = TimeSpan.FromHours(Math.Ceiling(timeOfDay.TotalHours));
var delta = (nextFullHour - timeOfDay).TotalSeconds;
//Completely misread. Completely re-writing
I woudl just do something Like this
int minutesToNextHour = 60 - DateTime.Now.Minutes;
int secondsToNextHour = minutesToNextHour * 60;
You don't have to mess around with ceilings and floors. The DateTime.Hour property represents whole hours (it is an integer beteen 0 and 23) of the time of the day represented by the DateTime. You can use this and the DateTime.Date property to strip the components of the DateTime you don't want (sub-hour data) and then just subtract as necessary to produce a TimeSpan.
var now = DateTime.Now;
var timeToNextHour = now.Date.AddHours(now.Hour + 1) - now;
You can of course extract the TotalSeconds component of the resulting TimeSpan if you want the result in seconds.
This seems to be the most simple:
3600 - DateTime.Now.TimeOfDay.TotalSeconds % 3600
(if you want it in whole numbers - integer - then prefix DateTime.Now... with (int).
So you'd need to subtract the 'remainder' minutes, find the difference, and multiply that by 60, right?
How about this:
var currentTime = DateTime.Now;
var hour = currentTime.AddHours(1).Hour;
var newTime = Convert.ToDateTime(hour + ":00");
var timespan = newTime.Subtract(currentTime);
var secondsDiff = timespan.TotalSeconds;
TimeSpan sec = new TimeSpan(0, 0, 3600 - (DateTime.Now.Minute * 60));
How about:
var now = DateTime.Now;
int secondsTillNextHour = (60 - now.Minute)*60+(60-now.Second);
Or (maybe clearer):
int SecondsTillNextHour = 3600 - 60*now.Minute - now.Second;
A more readable version:
public double SecondsToNextHour()
{
return SecondsToNextHour( DateTime.Now );
}
public double SecondsToNextHour( DateTime moment )
{
DateTime currentHour = new DateTime( moment.Year, moment.Month, moment.Day, moment.Hour, 0, 0 );
DateTime nextHour = currentHour.AddHours( 1 );
TimeSpan duration = nextHour - moment;
return duration.TotalSeconds;
}
TimeSpan result = (new DateTime(DateTime.Now.Year, DateTime.Now.Month,
DateTime.Now.Day, DateTime.Now.Hour + 1, 0, 0)).Subtract(DateTime.Now);
Basically here you are building a new DateTime that is one hour on from Now, with no minutes or seconds, then you subtract Now from this and have your result.
I would Timespan.Parse 08:30, add 1 hr to the object, then retrieve the hour part and build a new string with :00 as the minutes and reparse the new string. There may be a more efficient way to do this, but I find this technique clear to read.

Categories

Resources