I wanted to create a readonly struct that represents time in 24-hour format, so it has a method that is supposed to return a string of time (for example: "08:45" if 8 and 45 were passed respectively or "03:40" if 25 hours and 160 minutes were passed)
The problem is in the method, how do I return a string with values from the object inserted into it? I imagined something like return "0{stringtimeobj.hours}:0{stringtimeobj.minutes} but I can't really figure out how to format a string so that it has values from an object in it. Please help out!
using System;
namespace TimeStruct
{
public readonly struct Time
{
private readonly int hours2;
private readonly int minutes2;
public Time(int minutes)
: this()
{
this.minutes2 = minutes;
}
public Time(int hours, int minutes)
{
this.hours2 = hours;
this.minutes2 = minutes;
}
public int Hours
{
get
{
if (this.hours2 < 24)
{
return this.minutes2;
}
else if (this.hours2 == 24)
{
return 0;
}
else
{
double overflowtohours = ((Math.Truncate((double)this.minutes2 / 60) + 1) * 60) - 60;
return Convert.ToInt32(this.hours2 - ((Math.Truncate((double)(Convert.ToInt32(overflowtohours / 60) + this.hours2) / 24) + 1) * 24) - 24);
}
}
}
public int Minutes
{
get
{
if (this.minutes2 < 60)
{
return this.minutes2;
}
else if (this.minutes2 == 60)
{
return 0;
}
else
{
double overflowtohours = ((Math.Truncate((double)this.minutes2 / 60) + 1) * 60) - 60;
return Convert.ToInt32(this.minutes2 - overflowtohours);
}
}
}
public string ToString(int hours3, int minutes3)
{
Time stringtimeobj = new Time(hours3, minutes3);
return /* string "00:00" with hour and minute values from object stringtimeobj inserted */
}
}
}
You just need this implementation of your struct:
public readonly struct Time
{
private readonly int _minutes;
public Time(int minutes) : this(0, minutes) { }
public Time(int hours, int minutes)
{
_minutes = (hours * 60 + minutes) % (24 * 60);
}
public int Hours => _minutes / 60;
public int Minutes => _minutes % 60;
public override string ToString() => $"{this.Hours:00}:{this.Minutes:00}";
}
When I run this code:
Console.WriteLine(new Time(8, 45).ToString());
Console.WriteLine(new Time(25, 160).ToString());
I get the following output:
08:45
03:40
You can implement the standard ToString method like this:
public override string ToString()
{
return $"{this.Hours:00}:{this.Minutes:00}";
}
or equivalent:
public override string ToString()
{
return String.Format("{0:00}:{1:00}", this.Hours, this.Minutes);
}
The override keyword is required because you override the default ToString method
It doesn't need parameters, because it reads the local properties (this.Minutes and this.Hours - you can omit the "this."). Plus the standard ToString doesn't take parameters
The first one uses an interpolated string, the second example uses String.Format
In both cases the :00 means "format as two digits, adding leading 0's as needed" (docs)
Bonus: the debugger will use this method to display the value of this type
You can use built-in DateTime.ToString() with custom format. In your case it would look like:
int hour = 2;
int minute = 1;
DateTime time = new DateTime(2000, 1, 1, hour, minute, 0);
Console.WriteLine(time.ToString("HH:mm")); // 02:01
Yep , you can use it like this way below
DateTime time = new DateTime(2000, 1, 1, stringtimeobj.Hours,stringtimeobj.Minutes, 0);
return time.ToString("HH:mm");
We need to do some checking. If "stringtimeobj.hours" is greater than 9, which is 10 to 24, then "{stringtimeobj.hours}" can be used.
Also, if "stringTimeobj.minutes" is greater than 9, that is, 10 to 60, then "{stringtimeobj.minutes}" can be used.
Something like that.
Related
I wanted to return the remaining time until the next interval (KlineInterval). Basically, I want to avoid hard-coding stuff. My code works fine for 1-hour interval but it doesn't support the rest of the intervals. I want it to support all of them and if there is a way to do that in a not hard-coded way (those ugly ifs).
Is this possible?
public enum KlineInterval
{
OneMinute = 0,
ThreeMinutes = 1,
FiveMinutes = 2,
FifteenMinutes = 3,
ThirtyMinutes = 4,
OneHour = 5,
TwoHour = 6,
FourHour = 7,
SixHour = 8,
EightHour = 9,
TwelveHour = 10,
OneDay = 11,
ThreeDay = 12,
OneWeek = 13,
OneMonth = 14
}
public static double RemainingSecondsUntilNextInterval(KlineInterval interval)
{
if (interval == KlineInterval.FiveMinutes)
{
double currentTimeUnixTimestamp = DateTimeToUnixTimestamp(DateTime.Now);
int minutesInSeconds = 5 * 60;
return minutesInSeconds - (currentTimeUnixTimestamp % minutesInSeconds);
}
else if (interval == KlineInterval.OneHour)
{
var timeOfDay = DateTime.Now.TimeOfDay;
var nextFullHour = TimeSpan.FromHours(Math.Ceiling(timeOfDay.TotalHours));
return (nextFullHour - timeOfDay).TotalSeconds;
}
else
{
throw new NotSupportedException("Interval not supported.");
}
}
Edit:
using System;
class Program
{
private static double DateTimeToUnixTimestamp(DateTime dateTime)
{
return (TimeZoneInfo.ConvertTimeToUtc(dateTime) -
new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
}
private static double RemainingSecondsUntilXMinutes(int minutes)
{
double currentTimeUnixTimestamp = DateTimeToUnixTimestamp(DateTime.Now);
return (minutes * 60) - (currentTimeUnixTimestamp % (minutes * 60));
}
static void Main(string[] args)
{
// Remaining seconds until the next hour
var nextHour = RemainingSecondsUntilXMinutes(60);
// Remaining seconds until the next 5 minutes
var nextFiveMinutes = RemainingSecondsUntilXMinutes(5);
Console.ReadKey();
}
}
#Max, is that what you wanted to say?
It's important to be aware of the fact that there will need to be some place where each of the enums you have declared are mapped to their expected behaviour. (There is also the option of parsing the string values of the enums. This is very hacky and I would advise against it.) This means that you will need to have some control flow that splits based on the enum type. You can do this with either an if statement or a switch statement (switch statements are more common for this purpose, but both work fine).
Still, it's not necessary to have 3 lines of code within each branch. You can bring it down to just one line per branch. When trying to reduce the amount of code duplication, it is helpful to look at which parts are the same in the different cases and which are different.
In this case, it's useful to first convert the enum into a Timespan and then implement some logic based on this Timespan that computes the number of remaining seconds. One method could deal with converting the enum to a Timespan, and the other could calculate the remaining seconds in the interval based on the Timespan, like so:
private static double RemainingSecondsUntilNextInterval(Timespan interval) {
// ...
}
private static Timespan TimespanFromKlineInterval(KlineInterval interval) {
switch(interval){
case KlineInterval.OneMinute:
// ...
}
public static double RemainingSecondsUntilNextInterval(KlineInterval interval) {
return RemainingSecondsUntilNextInterval(TimespanFromKlineInterval(interval));
}
For example:
2011-08-11 16:59 becomes 2011-08-11 16:30
DateTime RoundDown(DateTime dt, TimeSpan d)
{
return new DateTime((dt.Ticks / d.Ticks) * d.Ticks);
}
Example:
var dt1 = RoundDown(DateTime.Parse("2011-08-11 16:59"), TimeSpan.FromMinutes(30));
// dt1 == {11/08/2011 16:30:00}
var dt2 = RoundDown(DateTime.Parse("2011-08-11 17:00"), TimeSpan.FromMinutes(30));
// dt2 == {11/08/2011 17:00:00}
var dt3 = RoundDown(DateTime.Parse("2011-08-11 17:01"), TimeSpan.FromMinutes(30));
// dt3 == {11/08/2011 17:00:00}
I would say something like that
var time = DateTime.Now;
var rounded = time.AddMinutes(
time.Minute>30 ? -(time.Minute-30) : -time.Minute)
And you could even do your own extension
public static class TimeHelper {
public static DateTime RoundDown (this DateTime time)
{
return time.AddMinutes(
time.Minute>30 ? -(time.Minute-30) : -time.Minute);
}
}
EDIT
This function cut's also the seconds / milliseconds if necessary. Thanks for the hint.
public static DateTime RoundDown(this DateTime time)
{
return time.Subtract(
new TimeSpan(0, 0, time.Minute > 30 ? (time.Minute - 30) : time.Minute,
time.Second, time.Millisecond));
}
A more generic solution rounding to the nearest time span:
public static DateTime RoundUp(this DateTime dt, TimeSpan d)
{
var delta = (d.Ticks - (dt.Ticks % d.Ticks)) % d.Ticks;
return new DateTime(dt.Ticks + delta);
}
public static DateTime RoundDown(this DateTime dt, TimeSpan d)
{
var delta = dt.Ticks % d.Ticks;
return new DateTime(dt.Ticks - delta);
}
public static DateTime RoundToNearest(this DateTime dt, TimeSpan d)
{
var delta = dt.Ticks % d.Ticks;
bool roundUp = delta > d.Ticks / 2;
return roundUp ? dt.RoundUp(d) : dt.RoundDown(d);
}
It would be used this way:
var date = new DateTime(2010, 02, 05, 10, 35, 25, 450); // 2010/02/05 10:35:25
var rounded = date.RoundToNearest(TimeSpan.FromMinutes(30)); // 2010/02/05 10:30:00
More in this response.
DateTime newDateTime = new DateTime(oldDateTime.Year, oldDateTime.Month, oldDateTime.Day
,oldDateTime.Hour, (oldDateTime.Minute / 30) * 30, 0);
Exploiting some math
var input = DateTime.Now; // or however you get DateTime
var rounded = input.AddMinutes(-input.TimeOfDay.TotalMinutes % 30d);
Note that TimeOfDay is a TimeSpan and its TotalMinutes property is a double and that the modulus operator functions on doubles like follows:
47.51 % 30d == 17.51
16.2 % 30d == 16.2
768.7 % 30d == 18.7
You could change the 30d to any value you like other than zero. Changing to 15 rounds down to 15 minute intervals for instance. Changing from 30d to -30d, didn't change the results from the tests that I ran.
You could create a rounding extension method (providing this rounding method for all DateTime values):
public static class DateTimeExtensions
{
public static DateTime Round(this DateTime self, double minutesInInterval)
{
if (minutesInInterval == 0d) throw new ArgumentOutOfRangeException("minutesInInterval");
return self.AddMinutes(-self.TimeOfDay.TotalMinutes % minutesInInterval);
}
}
There is no such function available
You can refer to : How can I round up the time to the nearest X minutes?
IF needed you can create one yourself
DateTime RoundUp(DateTime dt, TimeSpan d)
{
return new DateTime(((dt.Ticks - d.Ticks + 1) / d.Ticks) * d.Ticks + d.Ticks);
}
eg:
var dt1 = RoundUp(DateTime.Parse("2011-08-11 16:59"), TimeSpan.FromMinutes(30));
// dt1 == {11/08/2011 16:30:00}
Here's how I handled it. Below is the base method and an overload to round to the nearest 30 minutes:
public static DateTime RoundMinutes(this DateTime value)
{
return RoundMinutes(value, 30);
}
public static DateTime RoundMinutes(this DateTime value, int roundMinutes)
{
DateTime result = new DateTime(value.Ticks);
int minutes = value.Minute;
int hour = value.Hour;
int minuteMod = minutes % roundMinutes;
if (minuteMod <= (roundMinutes / 2))
{
result = result.AddMinutes(-minuteMod);
}
else
{
result = result.AddMinutes(roundMinutes - minuteMod);
}
return result;
}
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();
}
I'm working on a date-time system for a game. In the interest of reusability, I decided not to use the DateTime included in .NET. It reflects Earth time, whereas I'd like to be able to have arbitrary values for things like hoursPerDay, and have, say, 10 months in a year, with any number of days each. For now I don't care about things like leap years, or time zones.
I'm having trouble figuring out how to get the current day of the month (MM-DD-YYYY). The code in question is the DayOfMonth property's get method.
public sealed class Time
{
int _ticks = 0;
int _ticksPerSecond = 30;
int _secondsPerMinute = 60;
int _minutesPerHour = 60;
int _hoursPerDay = 24;
readonly List<string> _days;
readonly Dictionary<string, int> _months;
// I haven't decided on ctor parameters yet, but we'd define base units
public Time()
{
// What we call the days of the week.
_days = new List<string> { "Monday", "Tuesday", "Wednesday" };
// What we call the months of the year, and the number of days each.
_months = new Dictionary<string, int>
{
{"January", 31},
{"February", 28},
{"March", 31}
};
}
public void Advance(int ticks)
{
_ticks += ticks;
}
// Number of ticks elapsed since epoch start
public int TotalTicks { get { return _ticks; } }
// Number of ticks elapsed during the current second
public int CurrentTicks { get { return _ticks % _ticksPerSecond; } }
public int TotalSeconds { get { return _ticks / _ticksPerSecond; } }
public int CurrentSeconds { get { return TotalSeconds % _secondsPerMinute; } }
public int TotalMinutes { get { return TotalSeconds / _secondsPerMinute; } }
public int CurrentMinutes { get { return TotalMinutes % _minutesPerHour; } }
public int TotalHours { get { return TotalMinutes / _minutesPerHour; } }
public int CurrentHours { get { return TotalHours % _hoursPerDay; } }
public List<string> Days { get { return _days; } }
public int TotalDays { get { return TotalHours / _hoursPerDay; } }
public int CurrentDay { get { return TotalDays % _days.Count; } }
public string DayOfWeek { get { return _days[CurrentDay]; } }
public int DayOfMonth
{
get
{
var d = 0;
while (d < TotalDays)
{
foreach (var month in Months)
{
var daysInMonth = month.Value;
while (daysInMonth > 0 && d < TotalDays)
{
d++;
daysInMonth--;
}
}
}
return d;
}
}
public Dictionary<string, int> Months { get { return _months; } }
public int TotalMonths { get { return TotalDays / _months.Values.Sum(); } }
public int CurrentMonth { get { return TotalMonths % Months.Count; } }
…
}
A. The code above doesn't work. It always returns a fixed value.
B. The only way I can think of to accomplish this is to iterate through each month, adding up days until we reach each month's day count, until we hit the total days elapsed. That doesn't seem very efficient to me, especially with the redundant checking if (d < TotalDays) above.
C. It seems like the more time that elapses, the longer it will take to find the DayOfMonth (and perhaps other) value(s).
I guess I'm looking for a paradigm shift, because I think I might be painting myself in a corner.
Update
For anyone who is interested in the (somewhat) working algorithms, I got this fixed by changing the DayOfMonth property to the following.
public int DayOfMonth
{
get
{
var d = TotalDays;
var found = false;
while(!found)
{
foreach (var month in _months)
{
if (d > month.Value) d -= month.Value;
else
{
found = true;
break;
}
}
}
return d;
}
}
The TotalMonths property needed a similar fix.
public int TotalMonths
{
get
{
var d = TotalDays;
var found = false;
var m = 0;
while(!found)
{
foreach (var month in _months)
{
if (d > month.Value)
{
d -= month.Value;
m++;
}
else
{
found = true;
break;
}
}
}
return m;
}
}
which enables MonthOfYear:
public string MonthOfYear { get { return _months.Keys.ToList()[CurrentMonth]; } }
So far things seem to be in order. I suspect there's a more elegant solution, perhaps involving Yield (which I don't yet understand), so if anyone comes up with something interesting I'd love to hear about it.
If you don't have leap years, it's relatively easy:
You can work out how many ticks are in a year, and use the % operator to work out the tick within the year for a given value
Divide by the number of ticks in a day to get the day within the year
You could then have a statically-constructed array for the "start day-of-year of each month", do a binary search to find the right month, and then subtract the start day-of-year to find the day within the month. Alternatively, just iterate over the months and subtract the number of days in each month until you find your value is less than the number of days within the month you're considering.
One option: use Noda Time and implement your own CalendarSystem... which probably isn't too hard, except for understanding the system to start with :) (Bear in mind that Noda Time isn't production-quality yet, but you could have fun with it...)
I've not used it myself in the past, but the System.Globalization.Calendar class may help you.
It looks as though you can create your own "calendars" for use with DateTime etc. Their example implementations include things like "ChineseLunisolarCalendar, so perhaps instead of doing all the hard work yourself, you can create some sort of "calendar specification" so you can still use the built-in types?
this is how the DateTime does it when you look at the decompiled assembly:
private int GetDatePart(int part)
{
long internalTicks = this.InternalTicks;
int num1 = (int)internalTicks / 864000000000L;
int num2 = num1 / 146097;
num1 = num1 - (num2 * 146097);
int num3 = num1 / 36524;
if (num3 == 4)
{
num3 = 3;
}
num1 = num1 - (num3 * 36524);
int num4 = num1 / 1461;
num1 = num1 - (num4 * 1461);
int num5 = num1 / 365;
if (num5 == 4)
{
num5 = 3;
}
if (part == 0)
{
return ((((num2 * 400) + (num3 * 100)) + (num4 * 4)) + num5) + 1;
}
num1 = num1 - (num5 * 365);
if (part == 1)
{
return num1 + 1;
}
bool flag = num5 == 3 && ((num4 == 24 && num3 == 3) || !(num4 == 24));
int[] daysToMonth366 = DateTime.DaysToMonth366;
int num6 = num1 >> 6;
while (num1 >= daysToMonth366[num6])
{
num6++;
}
if (part == 2)
{
return num6;
}
return (num1 - daysToMonth366[(num6 - 1)]) + 1;
}
internal long InternalTicks
{
get
{
return this.dateData & 4611686018427387903L;
}
}
public int Day
{
get
{
return this.GetDatePart(3);
}
}
I take the difference between two DateTime fields, and store it in a TimeSpan variable, Now I have to round-off the TimeSpan by the following rules:
if the minutes in TimeSpan is less than 30 then Minutes and Seconds must be set to zero,
if the minutes in TimeSpan is equal to or greater than 30 then hours must be incremented by 1 and Minutes and Seconds must be set to zero.
TimeSpan can also be a negative value, so in that case I need to preserve the sign..
I could be able to achieve the requirement if the TimeSpan wasn't a negative value, though I have written a code I am not happy with its inefficiency as it is more bulky ..
Please suggest me a simpler and efficient method.
Thanks regards,
This is my code which works fine, when TimeSpan is not negative value ..
TimeSpan time_span = endTime.Subtract(startTime);
TimeSpan time_span1;
if (time_span.Minutes >= 30)
{
time_span1 = new TimeSpan(time_span.Hours + 1, 0, 0);
}
else
{
time_span1 = new TimeSpan(time_span.Hours, 0, 0);
}
time_span1 will contain the result ..
How about:
public static TimeSpan Round(TimeSpan input)
{
if (input < TimeSpan.Zero)
{
return -Round(-input);
}
int hours = (int) input.TotalHours;
if (input.Minutes >= 30)
{
hours++;
}
return TimeSpan.FromHours(hours);
}
You can use
double v = span.TotalHours;
v = Math.Round(v, MidpointRounding.AwayFromZero);
span = TimeSpan.FromHours(v);
It depends on whether I understood your rules for negative values correctly.
TimeSpan is immutable, so you have to create a new one. This is also a perfect case for using extension methods in C#:
public static class TimeSpanUtility
{
public static TimeSpan Round( this TimeSpan ts )
{
var sign = ts < TimeSpan.Zero ? -1 : 1;
var roundBy = Math.Abs(ts.Minutes) >= 30 ? 1 : 0;
return TimeSpan.FromHours( ts.TotalHours + (sign * roundBy) );
}
}
// usage would be:
var someTimeSpan = new TimeSpan( 2, 45, 15 );
var roundedTime = someTimeSpan.Round();