2012-11-11 4 views
6

Sto cercando di ottenere tutti gli offset visualizzati in un fuso orario, in un intervallo. Di seguito è la funzione che ho usato per realizzare questo, so che è possibile utilizzare TimeZoneInfo.BaseUtcOffset per ottenere il UTC offset per un fuso orario durante l'ora solare, ma non esiste un metodo simile per ottenerne uno durante l'ora legale a meno che non si passi un particolare punto temporale DST al metodo GetUTCOffset().Come ottenere l'offset UTC durante l'ora legale in .NET

static void GetOffsets(DateTime startTime, DateTime endTime, TimeZoneInfo tz) 
{ 
    var result = new HashSet<int>(); 
    var adjRules = tz.GetAdjustmentRules(); 
    result.Add(tz.BaseUtcOffset); 

    foreach (var adjustmentRule in adjRules) 
    { 
     if ((startTime >= adjustmentRule.DateStart && startTime <= adjustmentRule.DateEnd) || (endTime >= adjustmentRule.DateStart && endTime <= adjustmentRule.DateEnd) || 
      (stTime <= adjustmentRule.DateStart && endTime >= adjustmentRule.DateEnd)) 
     { 
      if(adjustmentRule.DaylightDelta != TimeSpan.Zero) 
      { 
       if (!result.Contains(tz.BaseUtcOffset + adjustmentRule.DaylightDelta)) 
         result.Add((tz.BaseUtcOffset + adjustmentRule.DaylightDelta)); 
      } 
     } 
    } 

    foreach (var res in result) 
    { 
     Console.WriteLine(res); 
    } 
} 

Per favore fatemi sapere se c'è un modo migliore per farlo.

+0

Il codice è misterioso. Se desideri tutte le correzioni possibili, rimuovi semplicemente i test su startTime e endTime. Come il risultato potrebbe essere utile è molto difficile da vedere. –

+0

Ciao Hans, considerando che Microsoft ha delle regole che determinano il DaylightDelta, ho pensato che per questo metodo fosse generico dovevo considerare un intervallo o usare DateTime.Min e DateTime.Max come intervalli per ottenere tutti gli offset precedenti. – vinasi

+0

use case: utilizzo queste informazioni offSet per passare a una stored procedure db del server sql, l'interfaccia utente che gestisce un report ha un menu a discesa che elenca tutti i 24 "1 ora: timeintervals in un giorno (ex 1PM -2PM), lo converto in un offSet per passare ad un proc memorizzato che aveva dataTime salvato in UTC. se non fosse per questo caso d'uso, sto ancora cercando di sapere se c'è un altro modo per risolvere la mia domanda originale. – vinasi

risposta

-1

Se stai facendo qualcosa di complesso con le date dovresti dare un'occhiata a Noda Time (che è una porta della libreria Java Java di Joda). Ha un intero spazio dei nomi per affrontare i problemi spinosi del fuso orario.

0

Mi è venuto in mente questo metodo per ottenere gli offSet necessari per essere considerati per un intervallo UTC nell'intervallo di un anno per un timezoneinfo specificato. Trasmetto questa raccolta al database per filtrare i campi datetime salvati in UTC per gli input del filtro intervallo come ("1am - 2am"), l'ho provato per tutti i fusi orari nel mio sistema e ha funzionato bene. Sebbene questa non sia una risposta alla mia domanda iniziale poiché sto ancora usando le regole di regolazione per ottenere gli offset, ho provato a renderlo utilizzabile questa volta.

class Program 
{ 
    static void Main(string[] args) 
    { 
     foreach (var tz in TimeZoneInfo.GetSystemTimeZones()) 
     { 
      var result = GetUTCOffsetsByUTCIntervals(1900, 2012, tz); 
      Console.WriteLine(tz.DisplayName); 
      foreach (var tuple in result) 
      { 
       Console.WriteLine(tuple.Item1 + "  " + tuple.Item2 + " " + tuple.Item3); 
      } 
      Console.WriteLine("------------------------------------------------------------"); 
     } 
     Console.Read(); 
    } 

    public static List<Tuple<TimeSpan, DateTime, DateTime>> GetUTCOffsetsByUTCIntervals(int stYear, int endYear, TimeZoneInfo tz) 
    { 
     var cal = CultureInfo.CurrentCulture.Calendar; 
     var offsetsByUTCIntervals = new List<Tuple<TimeSpan, DateTime, DateTime>>(); 
     var adjRules = tz.GetAdjustmentRules(); 
     for (var year = stYear; year <= endYear && year < DateTime.MaxValue.Year && year >= DateTime.MinValue.Year; year++) 
     { 
      var adjRule = 
       adjRules.FirstOrDefault(
        rule => 
        rule.DateStart.Year == year || rule.DateEnd.Year == year || 
        (rule.DateStart.Year < year && rule.DateEnd.Year > year)); 

      var yrStTime = new DateTime(year, 1, 1); 
      var yrEndTime = yrStTime.AddYears(1).AddTicks(-1); 

      if (adjRule != null) 
      { 
       var tStDate = GetTransitionDate(adjRule.DaylightTransitionStart, year); 
       var tEnddate = GetTransitionDate(adjRule.DaylightTransitionEnd, year); 

       var stTsp = adjRule.DaylightTransitionStart.TimeOfDay.TimeOfDay; 

       var endTsp = adjRule.DaylightTransitionEnd.TimeOfDay.TimeOfDay; 

       if (yrStTime.Date == tStDate && yrStTime.TimeOfDay == stTsp) 
        yrStTime = yrStTime.Add(adjRule.DaylightDelta); 

       if (yrEndTime.Date == tEnddate && yrEndTime.TimeOfDay == endTsp) 
        yrEndTime = yrEndTime.Subtract(adjRule.DaylightDelta); 

       if (tStDate.Month > tEnddate.Month) 
       { 
        offsetsByUTCIntervals.Add(new Tuple<TimeSpan, DateTime, DateTime>(tz.BaseUtcOffset + adjRule.DaylightDelta, ConvertTimeToUtc(yrStTime, tz), ConvertTimeToUtc(tEnddate.AddTicks(endTsp.Ticks - 1), tz))); 
        offsetsByUTCIntervals.Add(new Tuple<TimeSpan, DateTime, DateTime>(tz.BaseUtcOffset, ConvertTimeToUtc(tEnddate.Add(endTsp.Subtract(adjRule.DaylightDelta)), tz), ConvertTimeToUtc(tStDate.AddTicks(stTsp.Ticks - 1), tz))); 
        offsetsByUTCIntervals.Add(new Tuple<TimeSpan, DateTime, DateTime>(tz.BaseUtcOffset + adjRule.DaylightDelta, ConvertTimeToUtc(tStDate.Add(stTsp.Add(adjRule.DaylightDelta)), tz), ConvertTimeToUtc(yrEndTime, tz))); 
       } 
       else 
       { 
        offsetsByUTCIntervals.Add(new Tuple<TimeSpan, DateTime, DateTime>(tz.BaseUtcOffset, ConvertTimeToUtc(yrStTime, tz), ConvertTimeToUtc(tStDate.AddTicks(stTsp.Ticks - 1), tz))); 
        offsetsByUTCIntervals.Add(new Tuple<TimeSpan, DateTime, DateTime>(tz.BaseUtcOffset + adjRule.DaylightDelta, ConvertTimeToUtc(tStDate.Add(stTsp.Add(adjRule.DaylightDelta)), tz), ConvertTimeToUtc(tEnddate.AddTicks(endTsp.Ticks - 1), tz))); 
        offsetsByUTCIntervals.Add(new Tuple<TimeSpan, DateTime, DateTime>(tz.BaseUtcOffset, ConvertTimeToUtc(tEnddate.Add(endTsp.Subtract(adjRule.DaylightDelta)), tz), ConvertTimeToUtc(yrEndTime, tz))); 
       } 
      } 
      else 
      { 
       offsetsByUTCIntervals.Add(new Tuple<TimeSpan, DateTime, DateTime>(tz.BaseUtcOffset, ConvertTimeToUtc(yrStTime, tz), ConvertTimeToUtc(yrEndTime, tz))); 
      } 
     } 
     return offsetsByUTCIntervals; 
    } 

    public static DateTime ConvertTimeToUtc(DateTime date, TimeZoneInfo timeZone) 
    { 
     if (date == null || timeZone == null) 
     { 
      return date; 
     } 
     DateTime convertedDate = TimeZoneInfo.ConvertTimeToUtc(date, timeZone); 
     return convertedDate; 
    } 

    //copy from msdn http://msdn.microsoft.com/en-us/library/system.timezoneinfo.transitiontime.isfixeddaterule.aspx 
    private static DateTime GetTransitionDate(TimeZoneInfo.TransitionTime transition, int year) 
    { 
     if (transition.IsFixedDateRule) 
      return new DateTime(year, transition.Month, transition.Day); 

     int transitionDay; 
     var cal = CultureInfo.CurrentCulture.Calendar; 
     var startOfWeek = transition.Week * 7 - 6; 
     var firstDayOfWeek = (int)cal.GetDayOfWeek(new DateTime(year, transition.Month, 1)); 
     var changeDayOfWeek = (int)transition.DayOfWeek; 

     if (firstDayOfWeek <= changeDayOfWeek) 
      transitionDay = startOfWeek + (changeDayOfWeek - firstDayOfWeek); 
     else 
      transitionDay = startOfWeek + (7 - firstDayOfWeek + changeDayOfWeek); 

     if (transitionDay > cal.GetDaysInMonth(year, transition.Month)) 
      transitionDay -= 7; 
     return new DateTime(year, transition.Month, transitionDay); 
    } 

    /* static void GetOffsets(DateTime startTime, DateTime endTime, TimeZoneInfo tz) 
    { 
     var result = new HashSet<string>(); 
     var adjRules = tz.GetAdjustmentRules(); 
     result.Add(tz.BaseUtcOffset.ToString()); 

     foreach (var adjustmentRule in adjRules) 
     { 
      if ((startTime >= adjustmentRule.DateStart && startTime <= adjustmentRule.DateEnd) 
        || (endTime >= adjustmentRule.DateStart && endTime <= adjustmentRule.DateEnd) 
       || (startTime <= adjustmentRule.DateStart && endTime >= adjustmentRule.DateEnd)) 
      { 
       if(adjustmentRule.DaylightDelta != TimeSpan.Zero) 
       { 
        if (!result.Contains((tz.BaseUtcOffset + adjustmentRule.DaylightDelta).ToString())) 
         result.Add((tz.BaseUtcOffset + adjustmentRule.DaylightDelta).ToString()); 
       } 
      } 
     } 
     Console.Write(tz.DisplayName + " "); 
     foreach (var res in result) 
     { 
      Console.Write(res); 
     } 
    }*/ 
} 
5

io sto cercando di ottenere tutte le compensazioni visto in un fuso orario, in un intervallo.

avrei fortemente consiglio di evitare di cercare di utilizzare TimeZoneInfo direttamente. Le regole di regolazione possono essere sorprendentemente imbarazzanti per alcune zone in alcuni anni, come ho scoperto a mie spese.

Mentre io sono di parte, suggerirei di usare Noda Time, che può avvolgere un TimeZoneInfo e fare il duro lavoro per voi, via BclDateTimeZone.FromTimeZoneInfo. La tua domanda non è completamente chiara in termini di requisiti, ma se puoi dire qualcosa in più su ciò che stai cercando di fare, posso scrivere il codice temporale Noda appropriato per te.

tua descrizione iniziale potrebbe essere implementato con qualcosa di simile:

public IEnumerable<ZoneInterval> GetZoneIntervalsInInterval 
    (this DateTimeZone zone, Interval interval) 
{ 
    Instant current = interval.Start; 
    while (current < interval.End && current != Instant.MaxValue) 
    { 
     ZoneInterval zi = zone.GetZoneInterval(current); 
     yield return zi; 
     current = zi.End; 
    } 
} 

Poi:

var zone = BclDateTimeZone.FromTimeZoneInfo(tzInfo); 
var offsets = zone.GetZoneIntervalsInInterval(interval) 
        .Select(zi => zi.WallOffset) 
        .Distinct(); 

Ecco assumendo che vuoi dire la stessa cosa "offset" come faccio io (vale a dire la differenza tra UTC e ora locale).

+0

Grazie per l'aiuto! @ Jon. Sfortunatamente non ho il lusso di aggiungere librerie di terze parti in questa fase del progetto mentre mancano 3 settimane alla versione di sviluppo, devo fare affidamento sui metodi .NET bcl per ora, come ho accennato nella mia domanda, il mio requisito è per filtrare i dati storici in cui i datetime sono in UTC in sql server con filtri temporali come '8am -9am'. Il fuso orario originariamente utilizzato per la conversione è fornito nella configurazione, quindi, dato un fuso orario, ho bisogno di conoscere gli offSet che ha visto in modo da poter utilizzare queste informazioni per filtrare i dati storici nel database. – vinasi

+0

Sono stato in grado di farlo funzionare con il mio post in "risposte", almeno che funziona per currentzon'es di interesse (EST, CST, PST, GBT), hai detto che le regole di regolazione sono scomode per alcune zone in alcuni anni. quando hai pensato che le informazioni di Timezoneinfo fossero scomode, a cosa si basa il tuo wrapper quando pensi che sia? – vinasi