Domanda

I'm trying to emulate the way Google Calendar and Outlook handle time selection where all time entries are 30 minutes apart. I almost have every thing working with functional tests except for one thing. My problem is that I can't figure out how to ensure I'm always rounding the current date up to 30 minutes.

I'm using the following code:

protected DateTime GetStartTime()
{
    var spanTicks = TimeSpan.FromMinutes(30).Ticks;
    var ticks = (Time.Now.Ticks + spanTicks - 1) / spanTicks;

    return new DateTime(ticks * spanTicks);
}

This works for the majority of the cases except for 12:00am (where I would expect this to return 12:30am) and say 8:56am (where I would expect it to return 9:00am).

How can I fix this?

È stato utile?

Soluzione

Let .NET take care of all the special cases for you:

var now = DateTime.Now;
DateTime result = now.AddMinutes(now.Minute >= 30 ? (60-now.Minute) : (30-now.Minute));
result = result.AddSeconds(-1* result.Second); // To reset seconds to 0
result = result.AddMilliseconds(-1* result.Millisecond); // To reset milliseconds to 0

Altri suggerimenti

Why bother with ticks? You can force the time to exactly :30 or :00, like this:

protected DateTime GetStartTime() {
    var now = DateTime.Now;
    int addHours;
    int minute;
    if (now.Minute >= 30) {
        addHour = 1;
        minute = 0;
    } else {
        addHour = 0;
        minute = 30;
    }
    return new DateTime(now.Year, now.Month, now.Day, hour, minute, 0).AddHours(addHours);
}

EDIT: ORIGINAL VERSION DID NOT WORK! Came from a bogous implementation in an old Mono!

New Version:

protected DateTime GetStartTime()
{
    DateTime dt=DateTime.Now;
    if (dt.Minute<30) 
    {
        dt=dt.AddMinutes(30-dt.Minute);
    }
    else 
    {
        dt=dt.AddMinutes(60-dt.Minute);
    }

    //dt now has the upcoming half-hour border

    //...

}

Have you tried something like the following? I have successfully used a similar method to round up to the next hour, minute, day etc...

private static readonly long _ticksIn30Mins = TimeSpan.FromMinutes(30).Ticks;

protected DateTime GetRoundedTime(DateTime inputTime) 
{     
    long currentTicks = inputTime.Ticks;
    return new DateTime(currentTicks.RoundUp(_ticksIn30Mins)); 
} 

public static class ExtensionMethods 
{     
    public static long RoundUp(this long i, long toTicks)     
    {         
        return (long)(Math.Round(i / (double)toTicks, 
                  MidpointRounding.AwayFromZero)) * toTicks; 
    } 
} 

This takes the RoundOff method from this previous question. You just need to modify it to always round up by using MidpointRoundingMode.AwayFromZero.

Finally to cope with the specific case of 12:00am becoming 12:30am then check if your before rounding and after rounding value are the same, and if so, increment the roundup amount of ticks (e.g. 30mins)

var currentTime = DateTime.Now;
var rounded = GetRoundedTime(currentTime); 
if (rounded == currentTime) 
{ 
    rounded = new DateTime(rounded.Ticks + _ticksIn30Mins); 
} 

For a tested console application that demonstrates this principle, please see the below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        private static readonly long _ticksIn30Mins = TimeSpan.FromMinutes(30).Ticks; 

        static void Main(string[] args)
        {
            WriteDateString(new DateTime(2012, 01, 18, 09, 45, 11, 152));
            WriteDateString(new DateTime(2012, 01, 18, 12, 15, 11, 999));
            WriteDateString(new DateTime(2012, 01, 18, 12, 00, 00, 000));

            Console.ReadLine();
        }

        private static void WriteDateString(DateTime dateTime)
        {
            Console.WriteLine("Before: {0}, After: {1}", dateTime, GetRoundedTime(dateTime));
        }

        private static DateTime GetRoundedTime(DateTime inputTime)
        {
            long currentTicks = inputTime.Ticks; 
            var rounded = new DateTime(currentTicks.RoundUp(_ticksIn30Mins));
            if (rounded == inputTime)
            {
                rounded = new DateTime(rounded.Ticks + _ticksIn30Mins);
            }
            return rounded;
        } 
    }

    public static class ExtensionMethods
    {
        public static long RoundUp(this long i, long toTicks)
        {
            return (long)(Math.Round(i / (double)toTicks, MidpointRounding.AwayFromZero)) * toTicks;
        }
    }  
}

Output:

Console output from date roundup example

Best regards,

                        String today = DateTime.Now.ToString("dd.MM.yyyy");
                        String hourMinute = DateTime.Now.ToString("HH:mm");

                        string[] sep = hourMinute.Split(":");
                        string hour = sep[0];
                        string minutes = sep[1];

                        int min = Int32.Parse(minutes);

                        if(min >= 30)
                        {
                            minutes = "30";
                        }
                        else
                        {
                            minutes = "00";
                        }

                        hourMinute = hour + ":" + minutes;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top