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 ");
Related
I am downloading the list of times from the database
var listaNadgodzinZPoprzMies = _ecpContext.Karta.Where(x => x.Login == userName && x.Rok == numerRoku && x.Miesiac < numerMiesiaca)
.Select(b => string.IsNullOrEmpty(b.SaldoNadgodzin) ? TimeSpan.Zero : TimeSpan.Parse(b.SaldoNadgodzin))
.ToList();
Adds all times
var sumaListaNadgodzinZPoprzMies = listaNadgodzinZPoprzMies.Aggregate(TimeSpan.Zero, (t1, t2) => t1 + t2);
And, I need to convert the number of minutes/ from the variable shown in the image (TimeSpan)
to string to format HHH:mm to other new variable
I scraped out these two functions some time ago in javascript ( I don't know how to convert them to c # ) (I don't know if it will work and whether it will be useful)
function msToTime(duration) {
const minutes = Math.floor((duration / (1000 * 60)) % 60), // 3.4 - 3, 3.5 - 3, 3.8 - 3
hours = Math.floor(duration / (1000 * 60 * 60));
return twoOrMoreDigits(hours) + ":" + twoOrMoreDigits(minutes);
}
function twoOrMoreDigits(n) {
return n < 10 ? '0' + n : n; // if (n < 10) { return '0' + n;} else return n;
}
anyone have any idea?
Here is an example:
TimeSpan time = new TimeSpan(200,1,23);
string strTime = $"{((int)time.TotalHours).ToString("D3")}:{time.Minutes.ToString("D2")}";
Console.WriteLine(strTime);
Output:
200:01
You want to format a timespan, you can achieve it by using this code:
var timespan = TimeSpan.FromMinutes(3180);
var result = timespan.ToString("hh:mm");
Console.WriteLine(result);
hh - hour in 24h format with leading zero
mm - minutes with leading zero
You can read more about timespan formatting here:
https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-timespan-format-strings
I am developing a Console application for converting the time from 12 hours format to 24 hours format:
input: 02:03:34PM expected output:14:03:34
But I am getting 14:3:34
Below is my code snippet:
string[] arr_temp = Console.ReadLine().Split(':');
string time = arr_temp[2].ToUpper().Contains("AM") ? "AM" : "PM";
string sec=string.Empty;
for (int i = 0; i < 2; i++)
{
sec+= arr_temp[2][i];
}
int _hour = Int32.Parse(arr_temp[0])==0?0: Int32.Parse(arr_temp[0]);
int _minute = Int32.Parse(arr_temp[1]) == 0 ? 0 : Int32.Parse(arr_temp[1]);
int _sec = Int32.Parse(sec)==0?0: Int32.Parse(sec);
_hour = (time == "PM") ? _hour += 12 : _hour += 0;
_hour = (_hour < 10) ? '0' + _hour : _hour;
_minute = (_minute < 10) ? '0' + _minute : _minute;
_sec = (_sec < 10) ? '0' + _sec : _sec;
I am not getting the expected output.
Please suggest.
Seems a bit complicated to me as there's a much simpler way to display your DateTime variable to either 12 or 24 hours format.
First you will have to convert your string to a valid DateTime object. There are parsing methods which you can use, but you will have first to validate the input string returned by the user as a valid date.
Use the following code in order to convert your string to DateTime:
string dateString = "03/01/2009 10:00 AM";
DateTime date = DateTime.Parse(dateString);
DateTime.Parse will throw an exception if input string is not in the right format. In order to make sure this doesn't happen, use DateTime.TryParse instead.
string dateString = "03/01/2009 10:00 AM";
DateTime dateTime;
if (DateTime.TryParse(dateString , out dateTime))
{
Console.WriteLine(dateTime);
}
Then you can display the DateTime variable and format it the way you want.
DateTime dateTime = DateTime.Now;
string str12Format = dateTime.ToString("hh:mm:ss tt"); //12 hours format
string str24Format = dateTime.ToString("HH:mm:ss tt"); //24 hours format
_hour in your code is an integer. You cannot concatenate string to an integer. But the reverse is possible.
So you should use this instead :
int _hour = Int32.Parse(arr_temp[0])==0?0: Int32.Parse(arr_temp[0]);
int _minute = Int32.Parse(arr_temp[1]) == 0 ? 0 : Int32.Parse(arr_temp[1]);
int _sec = Int32.Parse(sec)==0?0: Int32.Parse(sec);
_hour = (time == "PM") ? _hour += 12 : _hour += 0;
String _hourS = (_hour < 10) ? '0' + _hour : _hour;
String _minuteS = (_minute < 10) ? '0' + _minute : _minute;
Try using DateTime.TryParseExact followed by ToString, do not repeat Microsoft and reinvent the wheel:
string source = Console.ReadLine();
DateTime date;
// DateTime.TryParseExact supports many formats; that's why "12:34AM" will be accepted
// DateTimeStyles.AllowWhiteSpaces let us be nice and allow, say "11 : 34 : 47 PM"
if (DateTime.TryParseExact(
source,
new string[] {"h:m:stt" , "h:mtt", "htt", "H:m:s", "H:m", "H"},
CultureInfo.InvariantCulture, // or CultureInfo.CurrentCulture
DateTimeStyles.AssumeLocal | DateTimeStyles.AllowWhiteSpaces,
out date))
Console.WriteLine(date.ToString("HH:mm:ss"));
else
Console.WriteLine($"Sorry, {source} is not a valid date");
Just pass you input
public static TimeSpan ConvertToAMPM(DateTime date)
{
return TimeSpan.Parse(date.ToString("h:mm tt",
CultureInfo.InvariantCulture));
}
public static TimeSpan ConvertTo24Hour(string time)
{
var cultureSource = new CultureInfo("en-US", false);
var cultureDest = new CultureInfo("de-DE", false);
var dt = DateTime.Parse(time, cultureSource);
return TimeSpan.Parse(dt.ToString("t", cultureDest));
}
The other answers so far mostly address the example of handling a DateTime, but they do not explain why your code breaks.
What you are trying to do is to add a leading zero to an int variable just as you'd do it with a string.
The problem is that the internal representation of an int is just the number itself, and it carries no format information. As such, it cannot store information about leading zeros. This can only be done by using string, which do not represent a number but a collection of characters.
So the essence is that you need to see the data and its representation as two separate things. In general it's best to keep the data in its native form and only convert it at the very last moment when needed for display. This also allows you to respect cultural differences of the display representation.
Many basic data types (including int and DateTime etc.) are formattable. What this means is that they can be converted to a string (display) representation with respect to a pattern describing how this representation should be. For int, such a pattern can define that it needs to have a leading zero like so:
string _hourDisplay = _hour.ToString("00");
Hi Nishank, Use this code :
string[] arr_temp = Console.ReadLine().Split(':');
string time = arr_temp[2].ToUpper().Contains("AM") ? "AM" : "PM";
string sec = arr_temp[2].Substring(0, 2);
string _hour = "";
if (time == "PM" && Int32.Parse(arr_temp[0]) < 12)
_hour = (Int32.Parse(arr_temp[0]) + 12).ToString("D2");
else if (time == "AM" && Int32.Parse(arr_temp[0]) == 12)
_hour = "00";
else
_hour = Int32.Parse(arr_temp[0]).ToString("D2");
string _minute = Int32.Parse(arr_temp[1]) == 0 ? "00" : Int32.Parse(arr_temp[1]).ToString("D2");
string _sec = Int32.Parse(sec) == 0 ? "00" : Int32.Parse(sec).ToString("D2");
string outputTime = _hour + ":" + _minute + ":" + _sec + "" + time;
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 found VBScript code to retrieve a user's Outlook status and display in a list box. I need it to be C# and there are no conversion tools online:
Set objOutlook = CreateObject("Outlook.Application")
Set objNamespace = objOutlook.GetNamespace("MAPI")
Set objRecipient = objNameSpace.CreateRecipient("kenmyer")
strFreeBusyData = objRecipient.FreeBusy(#11/11/2014#, 60)
dtmStartDate = #11/11/2014#
For i = 1 to Len(strFreeBusyData) Step 24
Wscript.Echo dtmStartDate
strDay = Mid(strFreeBusyData, i, 24)
For x = 1 to 12
If x = 1 Then
strTime = "12 AM: "
Else
strTime = x - 1 & " AM: "
End If
intFreeBusy = Mid(strDay, x, 1)
If intFreeBusy = 1 Then
strFreeBusy = "Busy"
Else
strFreeBusy = "Free"
End If
Wscript.Echo strTime & strFreeBusy
Next
For x = 13 to 24
If x = 13 Then
strTime = "12 PM: "
Else
strTime = x - 13 & " PM: "
End If
intFreeBusy = Mid(strDay, x, 1)
If intFreeBusy = 1 Then
strFreeBusy = "Busy"
Else
strFreeBusy = "Free"
End If
Wscript.Echo strTime & strFreeBusy
Next
Wscript.Echo
dtmStartDate = dtmStartDate + 1
If dtmStartDate > #11/12/2014# Then
Exit For
End If
Next
The end result would look like this in my C# application under the appointments list box:
11/11/2014
8 AM: Free
9 AM: Free
10 AM: Free
11 AM: Free
12 PM: Free
1 PM: Free
2 PM: Free
3 PM: Free
4 PM: Free
5 PM: Busy
6 PM: Free
What I have so far:
private void userschedule()
{
Outlook.Application oApp = new Outlook.Application();
Microsoft.Office.Interop.Outlook.NameSpace ns = oApp.Application.Session;
Outlook.Recipient recipient = ns.CreateRecipient(username.Text);
DateTime datetime = DateTime.Now;
string freeBusy = recipient.AddressEntry.GetFreeBusy(datetime, 60, true);
string status = freeBusy.Substring(0, 1);
textBox1.Text = status;
}
How can I convert this to C#? The other set of code is similar:
For i = 1 to Len(strFreeBusyData) Step 24
Wscript.Echo dtmStartDate
strDay = Mid(strFreeBusyData, i, 24)
For x = 1 to 12
If x = 1 Then
strTime = "12 AM: "
Else
strTime = x - 1 & " AM: "
End If
intFreeBusy = Mid(strDay, x, 1)
If intFreeBusy = 1 Then
strFreeBusy = "Busy"
Else
strFreeBusy = "Free"
End If
Wscript.Echo strTime & strFreeBusy
Next
Newly Edited Code ( Final Answer to my Question)
private void userstatus()
{
{
{
Outlook.Application oApp = new Outlook.Application();
Microsoft.Office.Interop.Outlook.NameSpace ns = oApp.Application.Session;
Outlook.Recipient recipient = ns.CreateRecipient(username.Text);
DateTime datetime = DateTime.Now; // gets current date and time
DateTime startDate = DateTime.Today; //gets todays date
startDate.AddHours(8); //Skip to 8 am
string freeBusy = recipient.AddressEntry.GetFreeBusy(startDate, 60, true);
textBox1.Text = freeBusy;
foreach (char c in freeBusy) //iteration process
{
if (startDate.Hour == 0) //start at 12 AM
Contacts.Items.Add(startDate.ToString("dd/MM/yyyy"));
if (8 <= startDate.Hour && startDate.Hour <= 18) // 8AM to 6PM inclusive
{
listBox1.Items.Add(
String.Format(
"{0}: {1}",
startDate.ToString("hh tt"),
c == '0' ? "Free" : "Busy"));
}
startDate = startDate.AddHours(1);
if (startDate.Date > DateTime.Today)
break; // stop once we get to tomorrow.
}
}
}
private void button5_Click(object sender, EventArgs e)
{
userstatus();
}
The freeBusy string looks like this for the user I am getting the free/busy status for.
I got the below by doing this:
string freeBusy = recipient.AddressEntry.GetFreeBusy(datetime, 60, true);
textBox1.Text = freeBusy;
000000002200000000000000000000002200000000000000333333333333333333333333000000000000000000000000000000000000000000000000000000002000001100000000000000002000001000000000000000002000000000000000000000002022200000000000000000002000001000000000000000000000000000000000000000000000000000000000333333333333333333333333000000002001101000000000000000002000000000000000000000002000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000002000001100000000000000002000000000000000000000002000000000000000000000002020000000000000000000002000001000000000000000000000000000000000000000000000000000000000000000002000001100000000000000002000000000000000000000002000000000000000000000002000000000000000
The following for loop and logic in VBScript
For i = 1 to Len(strFreeBusyData) Step 24
Wscript.Echo dtmStartDate
strDay = Mid(strFreeBusyData, i, 24)
...
Next
would be equivalent to the following in C#
for(int i = 0; i < strFreeBusyData.Length; i+= 24)
{
Console.WriteLine(dtmStartDate);
var strDay = strFreeBusyData.Substring(i, 24);
...
}
One big difference between VBScript and C# is that VBScript tends to be 1 - based when dealing with strings, whereas C# is 0 based. Specifically the Mid function's first parameter wants an index into the string that is 1-based (1 would mean start at the first character) whereas string.Substrings first parameter should be 0-based (0 would mean start at the first character).
EDIT
After sleeping on it a better way of dealing with this in C# would be the following.
DateTime startDate = DateTime.Today;
string freeBusy = recipient.AddressEntry.GetFreeBusy(datetime, 60, true);
foreach(char c in freeBusy)
{
if(startDate.Hour == 0)
Console.WriteLine(startDate.ToString("dd/MM/yyyy"));
Console.WriteLine(
"{0}: {1}",
startDate.ToString("hh tt"),
c == '1' ? "Busy" : "Free");
startDate = startDate.AddHours(1);
}
This will get a DateTime that has today's date, but a time of 12 am. Then as you iterate over each character in the freeBusy string you will output the startDate formatted as dd/MM/yyyy when the hour is 0 or 12 am. Then you write out the hour in 12 hour time (hh) and the AM/PM designator (tt) and the word Busy if the character is "1" or Free otherwise. Then increment the startDate by one hour. This avoids the unnecessary inner for loops that the VBScript code has.
EDIT
Based on your updated code you should change this
listBox1.Items.Add("{0}: {1}") ;
listBox1.Items.Add(startDate.ToString("hh tt") + " " + (c == '1' ? "Busy" : "Free"));
listBox1.Items.Add(c == '1' ? "Busy" : "Free");
startDate.AddHours(1);
to this
listBox1.Items.Add(
string.Format(
"{0}: {1}",
startDate.ToString("hh tt"),
c == '1' ? "Busy" : "Free");
startDate = startDate.AddHours(1);
The "{0}: {1}" is for formatting and Console.WriteLine has an overload that takes a format string. The date wasn't changing because DateTime is immuteable and you have to capture the return value of AddHours, that was my bad.
Edit
Here's code that will only show items for today and only between 8am and 6pm
foreach (char c in freeBusy) //iteration process
{
if (startDate.Hour == 0 ) //start at 12 AM
Contacts.Items.Add(startDate.ToString("dd/MM/yyyy"));
if(8 <= startDate.Hour && startDate.Hour <= 18) // 8AM to 6PM inclusive
{
listBox1.Items.Add(
String.Format(
"{0}: {1}",
startDate.ToString("hh tt"),
c == '1' ? "Busy" : "Free"));
}
startDate = startDate.AddHours(1);
if(startDate.Date > DateTime.Today)
break; // stop once we get to tomorrow.
}
You can optimize this to break after you hit 6PM if you want. It could also be shortened to the following
DateTime startDate = DateTime.Today; //gets todays date
string freeBusy = recipient.AddressEntry.GetFreeBusy(datetime, 60, true);
Contacts.Items.Add(startDate.ToString("dd/MM/yyyy"));
startDate = startDate.AddHours(8); //Skip to 8 am
foreach (char c in freeBusy.Skip(8).Take(11)) // skip to 8AM and only go to 6PM
{
listBox1.Items.Add(
String.Format(
"{0}: {1}",
startDate.ToString("hh tt"),
c == '0' ? "Free" : "Busy"));
startDate = startDate.AddHours(1);
}
EDIT
Changed
c == '1' ? "Busy" : "Free"
to
c == '0' ? "Free" : "Busy"
I'm looking for a function in which I can enter 2 dates:
Starting like this:
public static String getDaysOrWeeksFromDateDiff(DateTime first, DateTime second)
{
//this function will substract the date from the other and result days if < 8 and weeks if > 7
return "1 week";
}
I've tried this function below, but it gives me a negative date (I've missed the year somehow in the calculation)
public static String getDaysOrWeeksFromDateDiff(DateTime first, DateTime second)
{
var cal = new GregorianCalendar(GregorianCalendarTypes.Localized);
var weeknr = cal.GetWeekOfYear(first, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
var weeknr2 = cal.GetWeekOfYear(second, CalendarWeekRule.FirstDay, DayOfWeek.Monday);
String strReturnValue;
if (weeknr == weeknr2)
{
var dagen = first.Day - second.Day;
strReturnValue = (dagen == 0 ? "today" : (dagen == 1 ? "1 day" : dagen + " days"));
}
else
{
strReturnValue = (weeknr - weeknr2 == 1 ? "1 week" : weeknr - weeknr2 + " weeks");
}
return strReturnValue;
}
You can subtract dates directly:
TimeSpan difference = second - first;
This returns a TimeSpan object, which you can query easily, e.g.
if (difference.Days >= 8) ...
Converting the number of Days into weeks should be a trivial exercise since every week has exactly 7 days...
(Note that Days will give you the number of days as an integer, whereas TotalDays will include fractional days.)
You can use Time span class
public static String getWeekOrMonthFromDateDiff(DateTime first, DateTime second)
{
var span = second - first;
if (span.Days <= 7)
return span.Days + " day(s)";
else
return span.Days / 7 + " week(s)";
}
You can simply do
TimeSpan diff = first - second;
Then you can do
int weekCount = diff.TotalDays / 7;
I've done something like this some days ago:
private string GetTimeSpan(DateTime toDateTime, DateTime fromDateTime)
{
TimeSpan ts = toDateTime- fromDateTime;
if (ts.Days < 0)
{
return "since " + ts.Days.ToString().Replace("-", string.Empty) + " Days";
}
else if (ts.Hours < 1)
{
return "in " + ts.Minutes + " Minutes";
}
else if (ts.Days < 1)
{
return "in " + ts.Hours + " Hours";
}
else if (ts.Days < 7)
{
return "in " + ts.Days + " Days";
}
else
{
return "in " + ts.Days / 7 + " Weeks";
}
}
Modify it the way you Need it :)
For a length of time like this, you should use TimeSpan instead of DateTime, and then calculate the number of weeks from the property TotalDays.
void func(DateTime t1, DateTime t2)
{
days = (t2 - t1).TotalDays;
weeks = days/7;
year = weeks/52;
}