2016-03-01 3 views
4

У нас есть система, которая представляет недели с указанием времени UTC, начинающегося &. Дата окончания времени из часового пояса Америки/Чикаго. Недели начинаются в полночь в субботу утром центрального времени и заканчивается в 23:59:59 в пятницу вечером центрального времени, поэтому их записи UTC в базе данных являются:Перевод времени UTC в любое время в будущем, учитывая переход на летнее время в часовом поясе

Week 1 - begin: 2015-10-24 05:00:00, end 2015-10-31 04:59:59 
Week 2 - begin: 2015-10-31 05:00:00, end 2015-11-07 05:59:59 
Week 3 - begin: 2015-11-07 06:00:00, end 2015-11-14 05:59:59 
Week 4 - begin: 2015-11-14 06:00:00, end 2015-11-21 05:59:59 
Week 5 - begin: 2015-11-21 06:00:00, end 2015-11-28 05:59:59 

Так из приведенных выше примеров недель, вы можете см. изменение времени от дневного света до стандартного времени между 10/31 & 11/7.

Мне нужно вернуть N недель с данной недели. Наши системы - это веб-роли C# Azure & и запускаются в облаке Azure (все вычислительные узлы - UTC). Моя логика заключается в том, чтобы начать стартовую неделю и добавить недельную работу дней к начальной дате/времени недели и запросить недели, у которых дата начала больше начальной даты начала и меньше или равна расчетной будущая дата.

var weeks = repository.Fetch(x => x.BeginDate <= nWeeksAheadUtc && x.BeginDate > week.BeginDate)l=; 

Это работает, за исключением случаев, когда в результате результирующего ответа происходит переход на летнее время. Из-за изменения времени, просим в течение следующих 3 недель с 1-й недели на основе добавления 21 дня к дате начала 1-й недели, приводит только к возврату Недели 2 & 3, поскольку рассчитанное будущее значение - 2015-11-14 05:00: 00, что исключает Неделя 4.

Я решил эту проблему, используя Nodatime следующим образом:

LocalDateTime localDateTime = LocalDateTime.FromDateTime(week.BeginDate); 
ZonedDateTime zonedDateTime = localDateTime.InZoneStrictly(DateTimeZoneProviders.Tzdb["UTC"]); 
zonedDateTime = zonedDateTime.WithZone(DateTimeZoneProviders.Tzdb["America/Chicago"]); 
DateTime centralDateTime = zonedDateTime.ToDateTimeUnspecified(); 
DateTime futureDateTime = centralDateTime.Add(TimeSpan.FromDays(weekCount*7)); 
localDateTime = LocalDateTime.FromDateTime(futureDateTime); 
zonedDateTime = localDateTime.InZoneStrictly(DateTimeZoneProviders.Tzdb["America/Chicago"]); 
DateTime nWeeksAheadUtc = zonedDateTime.ToDateTimeUtc(); 

var weeks = repository.Fetch(x => x.BeginDate <= nWeeksAheadUtc && x.BeginDate > week.BeginDate).OrderBy(x => x.RetailerWeekNumber).ToList(); 

в то время как функции, кажется громоздким и не очень интуитивным для разработчиков, которые будут следовать за мной в сохранении этого код. Есть ли более чистый способ сделать это через API Nodatime или (базовая дата/время C#), которую я пропускаю?

Добавление в запрашиваемом примере - я только что создал проект UnitTest для этого, и эти три класса:

Week.cs

using System; 

namespace NodaTimeTest 
{ 
    public class Week 
    { 
     public int Id { get; set; } 
     public DateTime BeginDate { get; set; } 
     public DateTime EndDate { get; set; } 
    } 
} 

WeekService.cs

using NodaTime; 
using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace NodaTimeTest 
{ 
    public class WeekService 
    { 
     private readonly List<Week> repository; 

     public WeekService() 
     { 
      this.repository = this.InitWeeks(); 
     } 

     public List<Week> GetNextWeeks(int weekId, int weekCount) 
     { 
      Week week = this.repository.First(x => x.Id == weekId); 

      // the meat - how to do this the right way? 
      LocalDateTime localDateTime = LocalDateTime.FromDateTime(week.BeginDate); 
      ZonedDateTime zonedDateTime = localDateTime.InZoneStrictly(DateTimeZoneProviders.Tzdb["UTC"]); 
      zonedDateTime = zonedDateTime.WithZone(DateTimeZoneProviders.Tzdb["America/Chicago"]); 
      DateTime centralDateTime = zonedDateTime.ToDateTimeUnspecified(); 
      DateTime futureDateTime = centralDateTime.Add(TimeSpan.FromDays(weekCount * 7)); 
      localDateTime = LocalDateTime.FromDateTime(futureDateTime); 
      zonedDateTime = localDateTime.InZoneStrictly(DateTimeZoneProviders.Tzdb["America/Chicago"]); 
      DateTime nWeeksAheadUtc = zonedDateTime.ToDateTimeUtc(); 

      var weeks = repository.Where(x => x.BeginDate <= nWeeksAheadUtc && x.BeginDate > week.BeginDate).OrderBy(x => x.Id).ToList(); 

      return weeks; 
     } 

     private List<Week> InitWeeks() 
     { 
      // sets up our list of 10 example dates in UTC encompassing America/Chicago daylight savings time change on 11/1 
      // this means that all weeks are 168 hours long, except week "4", which is 169 hours long. 
      var weeks = new List<Week>(); 
      DateTime beginDate = new DateTime(2015, 10, 10, 5, 0, 0, DateTimeKind.Utc); 

      for (int i = 1; i <= 10; i++) 
      { 
       DateTime endDate = beginDate.AddDays(7).AddSeconds(-1); 

       if (endDate.Date == new DateTime(2015, 11, 7, 0, 0, 0, DateTimeKind.Utc)) 
       { 
        endDate = endDate.AddHours(1); 
       } 

       weeks.Add(new Week { Id = i, BeginDate = beginDate, EndDate = endDate }); 

       beginDate = endDate.AddSeconds(1); 
      } 

      return weeks; 
     } 
    } 
} 

WeekServiceTest :

using Microsoft.VisualStudio.TestTools.UnitTesting; 
using System.Linq; 

namespace NodaTimeTest 
{ 
    [TestClass] 
    public class WeekServiceTest 
    { 
     private readonly WeekService weekService = new WeekService(); 

     [TestMethod] 
     public void TestGetNextThreeWeeksOverDaylightTimeChange() 
     { 
      var result = this.weekService.GetNextWeeks(2, 3); 

      Assert.AreEqual(3, result.ElementAt(0).Id); 
      Assert.AreEqual(4, result.ElementAt(1).Id); 
      Assert.AreEqual(5, result.ElementAt(2).Id); 
     } 

     [TestMethod] 
     public void TestGetNextThreeWeeksWithNoDaylightTimeChange() 
     { 
      var result = this.weekService.GetNextWeeks(5, 3); 

      Assert.AreEqual(6, result.ElementAt(0).Id); 
      Assert.AreEqual(7, result.ElementAt(1).Id); 
      Assert.AreEqual(8, result.ElementAt(2).Id); 
     } 
    } 
} 
+0

Вы уверены, что хотите сохранить UTC? По данным Национального центра ураганов «Переход на летнее время не влияет на UTC». – Kevin

+0

+ Кевин Да, это UTC. В то время как UTC не отражает изменения времени летнего времени, время, указанное (длина «недели» в часах), потому что оно основано на временах в центральном часовом поясе, на которые влияют изменения летнего времени. Эффективно весной неделя сокращается на один час, а осенью неделя удлиняется на один час, как вы можете видеть в приведенных моих примерах данных. – IceBox13

+2

@MattJohnson: Я действительно не уверен в том, чтобы поместить это в Code Review. Учитывая, что мы просим людей показать, что они придумали до сих пор, похоже, что если есть код «просто о работе, но на самом деле это не хорошо», который не должен требовать перехода в CodeReview. Основой этого вопроса является «Как мне достичь X?» а не «Пожалуйста, просмотрите мой код, который делает X». Но, как я уже сказал, я в двух умах ... –

ответ

4

Хорошо, если я вас правильно понял, я думаю, вы, вероятно, хотите что-то вроде:

var zone = DateTimeZoneProviders.Tzdb["America/Chicago"]; 
var instantStart = Instant.FromDateTimeUtc(week.BeginDate); 
var chicagoStart = instantStart.InZone(zone); 
var localEnd = chicagoStart.LocalDateTime.PlusWeeks(weekCount); 
var chicagoEnd = localEnd.InZoneLeniently(zone); 
var bclEnd = chicagoEnd.ToDateTimeUtc(); 

var result = repository 
    .Fetch(x => x.BeginDate >= week.BeginDate && x.BeginDate < bclEnd) 
    .OrderBy(x => x.RetailerWeekNumber) 
    .ToList(); 

Обратите внимание, что я сделал нижнюю границу включительно и верхняя граница эксклюзивные - это как правило, самый простой способ сделать что-то.

Вы могли бы, конечно, цепи много это вместе, если вы действительно хотите:

var zone = DateTimeZoneProviders.Tzdb["America/Chicago"]; 
var bclEnd = Instant.FromDateTimeUtc(week.BeginDate) 
    .InZone(zone) 
    .LocalDateTime 
    .PlusWeeks(weekCount) 
    .InZoneLeniently(zone) 
    .ToDateTimeUtc(); 

EDIT: выше, если ваш BeginDate действительно мгновенным, от которого вы хотите, чтобы начать получать данные. Похоже, на самом деле вы хотите добавить неделю к этому, чтобы начать.В этот момент это будет:

var zone = DateTimeZoneProviders.Tzdb["America/Chicago"]; 
var instantNow = Instant.FromDateTimeUtc(week.BeginDate); 
var chicagoNow = instantStart.InZone(zone); 
var localStart = chicagoNow.LocalDateTime.PlusWeeks(1); 
var localEnd = localEnd(weekCount); 
var bclStart = localStart.InZoneLeniently(zone).ToDateTimeUtc(); 
var bclEnd = localEnd.InZoneLeniently(zone).ToDateTimeUtc(); 

var result = repository 
    .Fetch(x => x.BeginDate >= bclStart && x.BeginDate < bclEnd) 
    .OrderBy(x => x.RetailerWeekNumber) 
    .ToList(); 
+0

Это именно то, что я искал. Вчера, когда я пытался делать расчеты, я добавлял дни и приближался к часу с сокращением времени. Похоже, что добавление недель делает этот сценарий лучше. Я заменил свой код вторым фрагментом с беглой цепочкой. Я должен был придерживаться своего лямбда-выражения так, как он получал следующие n недель, за исключением начальных недель. Все мои модульные тесты проходят - спасибо за вашу помощь! – IceBox13

+1

@ IceBox13: Добавление дней должно было работать нормально, если вы добавляли их в «LocalDateTime». Если вам действительно нужно использовать эксклюзивную нижнюю границу и инклюзивную верхнюю границу, это очень тревожно ... Я не уверен, что вы подразумеваете под «исключением начальных недель», но это звучит так, будто вам просто нужно добавить неделю к сначала начните (точно так же). –

+0

означает, что исходная неделя не должна быть частью набора результатов. Начиная с четвертой недели, дайте мне следующие 3 недели, и это должно обеспечить недели 5, 6 и 7. Вы продолжаете говорить об изменении границ моего выражения, что не было основной проблемой, которую я имел. Я всегда открыт для обучения больше - у вас есть статья или принцип/шаблон, чтобы указать мне, чтобы я мог понять, почему вы так сильно относитесь к тому, как кодируются границы? – IceBox13

 Смежные вопросы

  • Нет связанных вопросов^_^