0

I think the best way to describe this is to use examples. The range is between 4:00am and 4:00pm. At 0%, I want to get 4:00am back. At 100%, I want to get 4:00pm back. At 50%, I want to get 10:00am back.

The question is, how do I get this functionality with this signature?

string GetTime(percentage, startTime, endTime)
Daniel Kaplan
  • 62,768
  • 50
  • 234
  • 356
  • 1
    Are `startTime` and `endTime` of type `DateTime` or `string`? – phuzi Apr 04 '19 at 22:29
  • @phuzi DateTime – Daniel Kaplan Apr 04 '19 at 22:29
  • If startTime and endTime are `DateTimes`, then you can subtract them and get a `TimeSpan`. From the `TimeSpan`, you can get the `Ticks` property. Convert that to a `double`, multiply by your percentage, converting the result back to a `long`. Use that to construct a new `TimeSpan`. Finally add that new TimeSpan to the original startTime and return it as a DateTime – Flydog57 Apr 04 '19 at 22:32
  • 2
    If these are `DateTime` with `.Kind == DateTimeKind.Utc`, then sure - just subtract them. But if they are anything else then be careful because time zone transitions such as for daylight saving time and changes in standard time are *NOT* taken into account when subtracting `DateTime` values. Depending on your use case, you might be better off starting with `DateTimeOffset` values. – Matt Johnson-Pint Apr 04 '19 at 22:36
  • 3
    @MattJohnson good point. I'd like to ignore the concept of DST for this question and pretend it doesn't exist. – Daniel Kaplan Apr 04 '19 at 22:46

4 Answers4

4

Assuming both DateTimes are in the same timezone, and that there isn't a transition (DST started/ended, local hour changed, etc):

public static void Main()
{
    var startTime = new DateTime(2019, 01, 01, 4, 0, 0);
    var endTime = new DateTime(2019, 01, 01, 16, 0 , 0);

    // prints 10:00 AM
    Console.WriteLine(GetTime(0.5, startTime, endTime));
}

private static string GetTime(double percentage, DateTime startTime, DateTime endTime)
{
    // get the difference between the dates
    // you could use TotalSeconds or a higher precision if needed
    var diff = (endTime - startTime).TotalMinutes;

    // multiply the result by the percentage
    // assuming a range of [0.0, 1.0]
    double minutes = diff * percentage;

    // add the minutes (or precision chosen) to the startTime
    var result = startTime.AddMinutes(minutes);

    // and get the result
    return result.ToShortTimeString();
}

You can test that in this fiddle: https://dotnetfiddle.net/g4gHLJ

Camilo Terevinto
  • 31,141
  • 6
  • 88
  • 120
2

You should return a TimeSpan instead of a string. Here's how I would do it:

static TimeSpan GetTime(double percentage, TimeSpan startTime, TimeSpan endTime)
{
    var percentageInTicks = (long)((endTime - startTime).Ticks * percentage);
    return startTime.Add(TimeSpan.FromTicks(percentageInTicks));
}

Usage:

TimeSpan startTime = new TimeSpan(4, 0, 0);
TimeSpan endTime = new TimeSpan(16, 0, 0);
double[] percentages = new[] { 0, 0.5, 1 };

foreach (double percentage in percentages)
{
    var result = GetTime(percentage, startTime, endTime);
    Console.WriteLine(result.ToString());
}

Output:

04:00:00
10:00:00
16:00:00

Edit: You can do basically the same thing with DateTime if you wish:

static TimeSpan GetTime(double percentage, DateTime startDate, DateTime endDate)
{
    var percentageInTicks = (long)((endDate - startDate).Ticks * percentage);
    return startDate.TimeOfDay.Add(TimeSpan.FromTicks(percentageInTicks));
}

Usage:

DateTime startDate = DateTime.Today.Add(new TimeSpan(4, 0, 0));
DateTime endDate = DateTime.Today.Add(new TimeSpan(16, 0, 0));
double[] percentages = new[] { 0, 0.5, 1 };

foreach (double percentage in percentages)
{
    var result = GetTime(percentage, startDate, startDate);
    Console.WriteLine(result.ToString());
}
  • Returning a DateTime makes more sense than returning a TimeSpan. – Theodor Zoulias Apr 05 '19 at 00:13
  • @TheodorZoulias Why exactly? The OP wants a difference between two dates in percentage. So no, either a `TimeSpan` or `string` should be returned (Depending on the OP's wish). I do not understand the benefit of a `DateTime` here. – FranzHuber23 Apr 05 '19 at 06:08
  • @FranzHuber23 the OP gave a clear example of what he wants. For a range between 4:00am and 4:00pm and a percentage of 0% he wants 4:00am as a result. He doesn't want `TimeSpan.Zero`. – Theodor Zoulias Apr 05 '19 at 06:44
  • @TheodorZoulias: You're right. I've not read this correctly. For what the OP suggested, it does not make sense to return a `DateTime` either (Unless you format it later which in fact means returning a `string` like @Camilo Terevinto did). – FranzHuber23 Apr 05 '19 at 06:53
  • Returning a string is horrendous IMHO. Formatting belongs to the presentation layer. – Theodor Zoulias Apr 05 '19 at 07:06
  • @TheodorZoulias Check the output section in my answer. Do you see it returning `TimeSpan.Zero`, ever? – 41686d6564 stands w. Palestine Apr 05 '19 at 08:46
  • @Ahmed Abdelhameed no, it doesn't. My mistake. You can't format new TimeSpan(16, 0, 0) as 4:00am though. Formatting of TimeSpans is limited. – Theodor Zoulias Apr 05 '19 at 08:57
0

Here's how to do it with just DateTimes as inputs and outputs:

 public static DateTime GetTime(double percentage, DateTime startTime, DateTime endTime)
 {
     TimeSpan diff = endTime - startTime;
     long percentOfTimeAsTicks = (long) (percentage * diff.Ticks);
     return startTime + new TimeSpan(percentOfTimeAsTicks);
 }

As @MattJohnson pointed out, the two DateTimes need to be on the same basis (either both UTC or both in the same timezone with the same summer/winter status).

Using Ticks rather than seconds or minute will give you the most precision.

Here's a simple test:

  var start = DateTime.Today;
  var end = DateTime.Now;
  Debug.WriteLine(Struct128.GetTime(0.5, start, end));

That prints out a time just before 9:00 am (it's currently just before 6:00 pm here).

Flydog57
  • 6,851
  • 2
  • 17
  • 18
0

First you'll need to figure out the length of time between the start and end time and calculate the fraction of that that needs to be added to the start time...

DateTime GetTime(double percentage, DateTime startTime, DateTime endTime)
{
    var duration = endTime - startTime;
    var partDuration = percentage * (double)duration.TotalMilliseconds;
    return startTime.AddMilliseconds(partDuration);
}


GetTime(0.5, DateTime.Today.AddHours(4), DateTime.Today.AddHours(16)).Dump();
phuzi
  • 12,078
  • 3
  • 26
  • 50