Time difference in C#, 3-digit-format minutes - c#

I have a problem calculating difference between two timestamps, when minutes are represented with 3 digits, e.g. 180:22 = 180 minutes and 22 seconds.
So, can you help me how can I get difference between timestamps like:
180:22 and 122:11
or
232:21 and 31:34
etc.
UPDATE: I need to get difference between two times, defined as strings. What makes a problem is that minutes in those strings (times) are larger than 60, and they are over the limit. So I need to know how to find difference like in above examples (180:22 and 122:11, and 232:21 and 31:34)

Use System.TimeSpan structures:
var seconds=(new TimeSpan(0, 180, 22)-new TimeSpan(0, 122, 11)).TotalSeconds;
var minutes=(new TimeSpan(0, 232, 21)-new TimeSpan(0, 31, 34)).TotalMinutes;

Here's a class that will do this stuff:
public class CrazyTime
{
public TimeSpan TimeSpanRepresentation { get; set; }
public CrazyTime(TimeSpan timeSpan)
{
this.TimeSpanRepresentation = timeSpan;
}
public CrazyTime(string crazyTime)
{
// No error checking. Add if so desired
var pieces = crazyTime.Split(new[] { ':' });
var minutes = int.Parse(pieces[0]);
var seconds = int.Parse(pieces[1]);
TimeSpanRepresentation = new TimeSpan(0, minutes, seconds);
}
public static CrazyTime operator-(CrazyTime left, CrazyTime right)
{
var newValue = left.TimeSpanRepresentation - right.TimeSpanRepresentation;
return new CrazyTime(newValue);
}
public override string ToString()
{
// How should negative Values look?
return ((int)Math.Floor(TimeSpanRepresentation.TotalMinutes)).ToString() + ":" + TimeSpanRepresentation.Seconds.ToString();
}
}
Here's how it might be used:
var a = new CrazyTime("123:22");
var b = new CrazyTime("222:11");
var c = b - a;
Console.WriteLine(c);

This works:
string time1 = "180:22";
string time2 = "122:11";
TimeSpan span1 = getTimespan(time1);
TimeSpan span2 = getTimespan(time2);
TimeSpan diff = span1 - span2;
getTimespan just has to correctly parse the string. I decided on Regex to do that, but you could go any route, particularly if the delimiter ":" isn't ever going to change.
private static TimeSpan getTimespan(string time1)
{
Regex reg = new Regex(#"\d+");
MatchCollection matches = reg.Matches(time1);
if (matches.Count == 2)
{
int minutes = int.Parse(matches[0].Value);
int seconds = int.Parse(matches[1].Value);
return new TimeSpan(0, minutes, seconds);
}
return TimeSpan.Zero;
}

Related

Get time left until sunday 9:30 pm

What I want to do is basically in the question title.
This is what I've tried so far, unsuccessfully.
Note that I haven't implemented exact hour and minute yet (9:30 pm).
It actually seems to always return a value between 00:00:59 and 00:00:01 for some reason
DateTime nextSunday = DateTime.Today.AddDays(((int)DayOfWeek.Sunday - (int)DateTime.Today.DayOfWeek + 7) % 7) + new TimeSpan(21, 30, 0);
TimeSpan untilNextSunday = nextSunday - DateTime.Now;
await ReplyAsync($"It is in **{TimeSpan.FromSeconds(untilNextSunday.Seconds)}**");
Which equals to
var today = DateTime.Today;
var daysUntilSunday = ((int)DayOfWeek.Sunday - (int)today.DayOfWeek + 7) % 7;
var nextSunday = today.AddDays(daysUntilSunday);
var ts = new TimeSpan(21, 30, 0);
nextSunday = nextSunday.Date + ts;
TimeSpan untilNextSunday = nextSunday - DateTime.Now;
If possible, I'd also like to use Paris TimeZone.
I tend to find all of the DateTime.Today.AddDays(((int)DayOfWeek.Sunday - (int)DateTime.Today.DayOfWeek + 7) % 7) + new TimeSpan(21, 30, 0) arithmetic quite confusing. Instead I try to go with a more iterative approach that can be clearly reasoned about.
Try this:
public static DateTime GetNextDateTime(DateTime now, DayOfWeek targetDay, TimeSpan targetTime)
{
DateTime target = now.Date.Add(targetTime);
while (target < now || target.DayOfWeek != targetDay)
{
target = target.AddDays(1.0);
}
return target;
}
Now you can use it like this:
DateTime now = DateTime.Now;
DateTime target = GetNextDateTime(DateTime.Now, DayOfWeek.Sunday, new TimeSpan(21, 30, 0));
TimeSpan untilNextSunday = target.Subtract(now);
Here's an example using Noda Time, including time zone handling. It doesn't attempt to handle "interesting" situations where (say) you ask for the next 1:30am, and it's already 1:45am but the clock goes back at 2am - in which case the right answer is really "45 minutes" but this code will give you a week instead.
using System;
using NodaTime;
class Test
{
static void Main()
{
var duration = GetDurationToNext(
IsoDayOfWeek.Sunday, new LocalTime(21, 30),
DateTimeZoneProviders.Tzdb["Europe/Paris"],
SystemClock.Instance);
Console.WriteLine($"Duration: {duration}");
}
static Duration GetDurationToNext(
IsoDayOfWeek dayOfWeek,
LocalTime timeOfDay,
DateTimeZone zone,
IClock clock) // Or just take an instant
{
var now = clock.GetCurrentInstant();
var localNow = now.InZone(zone).LocalDateTime;
var localNext = localNow
.Date.With(DateAdjusters.NextOrSame(dayOfWeek))
.At(timeOfDay);
// Handle "we're already on the right day-of-week, but
// later in the day"
if (localNext <= localNow)
{
localNext = localNext.PlusWeeks(1);
}
var zonedNext = localNext.InZoneLeniently(zone);
var instantNext = zonedNext.ToInstant();
return instantNext - now;
}
}

Aggregate and Group by Datetime

Having this data (SQL):
I call (every minute with timer )data then group into 30 minutes interval with this method:
public void GetData()
{
try
{
using (crypto_dbEntities1 context = new crypto_dbEntities1(con))
{
var result = context.kraken_btc.Where(x => x.date >= LastRecordedPoint).ToList();
result = AggregateCandlesIntoRequestedTimePeriod(Period.Minute, Period.HalfAnHour, result);
foreach (var data in result.OrderBy(x => x.date))
{
data.date = DateTime.SpecifyKind(data.date.DateTime, DateTimeKind.Utc).ToUniversalTime();
Point pb = new Point
{
Date = data.date.DateTime.ToLocalTime(),
JDate = (long)data.javaDate,
Open = (double)data.open,
High = (double)data.hight,
Close = (double)data.close,
Low = (double)data.low,
Volume = (long)data.volume,
};
if (pb.Date <= LastRecordedPoint)
{
MainCollection.Last().Date = data.date.DateTime.ToLocalTime();
MainCollection.Last().Close = (double)data.close;
MainCollection.Last().High = (double)data.hight;
MainCollection.Last().Open = (double)data.open;
MainCollection.Last().Low = (double)data.low;
MainCollection.Last().Volume = (long)data.volume;
Debug.WriteLine(pb.Date + " Updated data ..");
}
else
{
MainCollection.Add(pb);
}
LastRecordedPoint = pb.Date;
}
}
}
catch (Exception err)
{
MessageBox.Show(err.ToString());
}
}
public enum Period
{
Minute = 1,
FiveMinutes = 5,
TenMinutes = 10,
QuarterOfAnHour = 15,
HalfAnHour = 30,
Hour = 60
}
private List<kraken_btc> AggregateCandlesIntoRequestedTimePeriod(Period rawPeriod, Period requestedPeriod, List<kraken_btc> candles)
{
int rawPeriodDivisor = (int)requestedPeriod;
candles = candles.GroupBy(g => new { TimeBoundary = new DateTime(g.date.Year, g.date.Month, g.date.Day, g.date.Hour, (g.date.Minute / rawPeriodDivisor) * rawPeriodDivisor, 0) })
.Select(s => new kraken_btc
{
date = s.Key.TimeBoundary,
hight = s.Max(z => z.hight),
low = s.Min(z => z.low),
open = s.First().open,
close = s.Last().close,
volume = s.Sum(z => z.volume),
})
.OrderBy(o => o.date)
.ToList();
return candles;
}
And it does the job of aggregating the data, but the problem is I have to wait 30 minutes for my serie finally update if you look at the last candle its time is 17.30 when database has data for 17.47 (im UTC +2.00).
So the question is how can I group data and start draw an incomplete candle at 18.00 like all exchange platform does...
The GroupBy statement rounds down the dates, so all data from 17:30 up to 17:47 is rounded down to 17:30.
I would move the code to calculate the TimeBoundary to its own method so you can unit test it fully, using the RoundUp/RoundDown methods in this question How can I round up the time to the nearest X minutes? by redent84
public static DateTime RoundUp(this DateTime dt, TimeSpan d)
{
var modTicks = dt.Ticks % d.Ticks;
var delta = modTicks != 0 ? d.Ticks - modTicks : 0;
return new DateTime(dt.Ticks + delta, dt.Kind);
}
public static DateTime RoundDown(this DateTime dt, TimeSpan d)
{
var delta = dt.Ticks % d.Ticks;
return new DateTime(dt.Ticks - delta, dt.Kind);
}

Convert Eorzea DateTime value to Earth time

A while ago I posted the following, which was gladly answered by olitee (all credit goes to him for the solution):
Convert DateTime value to Final Fantasy XIV Eorzea Game Time
I was trying to add features to my code and one would require to be able to put that Eorzea time (FFXIV) back to Earth time for alerts.
The following code, provided by olitee, was converting Earth time to Eorzea time just fine:
public static class EorzeaDateTimeExtention
{
public static DateTime ToEorzeaTime(this DateTime date)
{
const double EORZEA_MULTIPLIER = 3600D/175D;
long epochTicks = date.ToUniversalTime().Ticks - (new DateTime(1970, 1, 1).Ticks);
long eorzeaTicks = (long)Math.Round(epochTicks * EORZEA_MULTIPLIER);
return new DateTime(eorzeaTicks);
}
}
How would I achieve the opposite? I tried to revert the mathematical calculations but apparently it keeps giving me negative epochTicks which results in error whenever I try the conversion.
Apparently I am missing something or I got it wrong at some point.
My understanding of the ticks is quite limited.
Any help and/or tips would be greatly appreciated.
Thanks a lot in advance.
The ToEarthTime method should give you the earth time.
class Program
{
static void Main(string[] args)
{
var now = DateTime.Now;
var ff = now.ToEorzeaTime();
Console.WriteLine($"Now: {now} | FF: {ff}");
var ffNew = new DateTime(ff.Ticks, DateTimeKind.Utc);
var nowNew = ffNew.ToEarthTime();
Console.WriteLine($"Now: {nowNew} | FF: {ffNew}");
Console.ReadLine();
}
}
public static class Converter
{
private const double EORZEA_MULTIPLIER = 3600D / 175D;
public static DateTime ToEorzeaTime(this DateTime date)
{
long epochTicks = date.ToUniversalTime().Ticks - (new DateTime(1970, 1, 1).Ticks);
long eorzeaTicks = (long)Math.Round(epochTicks * EORZEA_MULTIPLIER);
return new DateTime(eorzeaTicks);
}
public static DateTime ToEarthTime(this DateTime date)
{
var epochTicks = (long) Math.Round(date.Ticks/EORZEA_MULTIPLIER);
var earthTicks = epochTicks + new DateTime(1970, 1, 1).Ticks;
var utc = new DateTime(earthTicks, DateTimeKind.Utc);
return utc.ToLocalTime();
}
}

Correct format to input date with time of the day

I want to be able to calculate the time in hours and minutes elapsed between, say, 12:35pm 02/13/2016 to 1:45pm 02/14/2016, but can't figure out the correct format to input it.
EDIT: Should add that the span between the times will be stored in an arraylist, one span per customer.
Basically, you need something like this:
var dateA = new DateTime(2016,2,13,12,35,0);
var dateB = new DateTime(2016,2,14,1,45,0);
var timespan = dateB - dateA;
var hours = timespan.Hours;
bar minutes = timespan.Minutes;
Here's how I would go about it:
Func<string, DateTime?> tryParse = t =>
{
DateTime output;
if (DateTime.TryParseExact(
t,
new [] { "h:mmtt MM/dd/yyyy" },
System.Globalization.CultureInfo.CurrentCulture,
System.Globalization.DateTimeStyles.AssumeLocal,
out output))
{
return output;
}
return null;
};
var dt1 = tryParse("12:35pm 02/13/2016");
var dt2 = tryParse("1:45pm 02/14/2016");
TimeSpan? ts = null;
if (dt1.HasValue && dt2.HasValue)
{
ts = dt2.Value.Subtract(dt1.Value);
}
if (ts.HasValue)
{
Console.WriteLine(
String.Format(
"{0} hours & {1} minutes",
ts.Value.Hours,
ts.Value.Minutes));
}

List of TimeSpan calculated by results from other list

I'm trying to create a list based on another list. The list looks like -
{T1, T1, T2, T2, T3, T3} however the integer is subject to change based on user inputs. I am trying to assign a TimeSpan value on a new list based on the index of the old list, and the integers will vary the result. For example, if the start time is given as 11:00, and the time gap given by the user is 5 (minutes), the new list should look like - {11:00, 11:00, 11:05, 11:05, 11:10; 11:10}
Here is my current function:
public List<string> TimeGet(List<string> heatList, TimeSpan startTimeSpan, TimeSpan timeGap)
{
List<string> timeList = new List<string>();
string timeToAddString;
for (int i = 0; i < heatList.Count; i++)
{
if (heatList[i].Contains("1"))
{
TimeSpan timeToAdd = startTimeSpan;
timeToAddString = Convert.ToString(timeToAdd);
timeList.Add(timeToAddString);
}
else
{
string resultString = Regex.Match(heatList[i], #"\d+").Value;
int resultInt = Int32.Parse(resultString);
timeGap = TimeSpan.FromMinutes(timeGap.Minutes * resultInt);
TimeSpan timeToAdd = startTimeSpan + timeGap;
timeToAddString = Convert.ToString(timeToAdd);
timeList.Add(timeToAddString);
}
}
}
I would like the new list to correspond to the values on heatlist depending on the integer based in each string on the list, and produce a timespan or time of day to correspond with it on a new list.
This function can be made much simpler, assuming every element in heatList follows that T\d+ pattern.
public IEnumerable<string> TimeGet(List<string> heatList, TimeSpan startTimeSpan, TimeSpan timeGap)
{
foreach (var element in heatList)
{
var input = element.Substring(1); // Takes everything from index 1 = the first digit in the string
int multiplier = Int32.Parse(input) - 1;
var additionalTime = new TimeSpan(0, (int)(timeGap.Minutes * multiplier), 0);
yield return (startTimeSpan + additionalTime).ToString();
}
}
Sample usage:
string[] sBaseList = { "T1", "T1", "T2", "T2", "T4", "T6" };
var sList = sBaseList.ToList();
TimeSpan startSpan = new TimeSpan(11, 0, 0);
TimeSpan gapSpan = new TimeSpan(0, 5, 0);
var result = TimeGet(sList, startSpan, gapSpan);
foreach (var s in result)
Console.WriteLine(s);
Console.ReadKey();
Result: 11:00:00, 11:00:00, 11:05:00, 11:05:00, 11:15:00, 11:25:00.
namespace Test
{
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
class MainClass
{
public static void Main (string[] args)
{
List<string> inputdata = new List<string> ();
List<TimeSpan> outputdata = new List<TimeSpan> ();
string input = null;
while ((input = Console.ReadLine ()) != string.Empty) {
inputdata.Add (input);
TimeSpan t = new TimeSpan (11, 0, 0) + new TimeSpan (0, Convert.ToInt32 (Regex.Match (input, "\\d+").ToString ()), 0);
outputdata.Add (t);
}
for (int i = 0; i < inputdata.Count; i++) {
Console.WriteLine ("Inputdata: {0}, Outputdata: {1}", inputdata [i], outputdata [i].ToString ());
}
}
}
}
Use LINQ
return heatList
.Select(t => TimeSpan.FromTicks(
(Int32.Parse(t.Substring(1))-1) * timeGap.Ticks
)
+ startTimeSpan).ToString("hh:mm")
.ToList();
Explanation:
.Select(t => ...) enumerates the strings from the heatList assigning each temperature to t.
t.Substring(1) skips the "T" in "T123".
Int32.Parse(t.Substring(1)) - 1 creates the number range 0, 1, 2 ...
* timeGap.Ticks gives the offset for a given temperature in ticks.
TimeSpan.FromTicks(...) + startTimeSpan) yields the resulting time by adding timespans.
.ToString("hh:mm") converts the resulting timespan into a string.
.ToList(); creates a new list.
Ticks is the unit TimeSpan uses to store time spans internally.
you can do that with simple linq statement
public static List<string> TimeGet(List<string> heatList, TimeSpan startTimeSpan, TimeSpan timeGap)
{
return heatList
.Select(x =>
startTimeSpan.Add(TimeSpan.FromMinutes(timeGap.Minutes*(int.Parse(x.Substring(1)) - 1)))
.ToString(#"hh\:mm")).ToList();
}
this will select an item from heatList one by one, parse the number in it and subtract 1 from it (so T1 result is 0 and T2 in 1 and ...), now add timeGap times the resulted number to startTimeSpan and format it in hh:mm format.

Categories