Работа с часами без минут при округлении DateTime до 30 минут - PullRequest
2 голосов
/ 18 января 2012

Я пытаюсь эмулировать способ, которым Календарь Google и Outlook обрабатывают выбор времени, когда все записи времени разделены на 30 минут. У меня почти все работает с функциональными тестами, кроме одного. Моя проблема в том, что я не могу понять, как обеспечить округление текущей даты до 30 минут.

Я использую следующий код:

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

    return new DateTime(ticks * spanTicks);
}

Это работает для большинства случаев, за исключением 12:00 (где я ожидал, что это вернется в 12:30) и скажем, 8:56 (где я ожидал, что это вернется в 9:00).

Как я могу это исправить?

Ответы [ 4 ]

2 голосов
/ 18 января 2012

РЕДАКТИРОВАТЬ: ОРИГИНАЛЬНАЯ ВЕРСИЯ НЕ РАБОТАЕТ! Пришел из заумной реализации в старом моно!

Новая версия:

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

    //...

}
2 голосов
/ 18 января 2012

Зачем беспокоиться о клещах? Вы можете принудительно установить время равным :30 или :00, например:

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);
}
1 голос
/ 18 января 2012

Пусть .NET позаботится обо всех особых случаях для вас:

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
1 голос
/ 18 января 2012

Вы пробовали что-то вроде следующего? Я успешно использовал аналогичный метод для округления до следующего часа, минуты, дня и т. Д. *

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; 
    } 
} 

Для этого используется метод RoundOff из предыдущего вопроса . Вам просто нужно изменить его, чтобы он всегда округлялся с помощью MidpointRoundingMode.AwayFromZero.

Наконец, чтобы справиться с конкретным случаем с 12:00 до 12:30, проверьте, совпадают ли значения до и после округления, и если да, увеличьте количество тиков (например, 30 минут)

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

Для протестированного консольного приложения, демонстрирующего этот принцип, см. Ниже:

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;
        }
    }  
}

Выход:

Console output from date roundup example

С уважением,

...