Skip to content

Commit e43ebd0

Browse files
committed
Added TimeZone related components to Library folder.
1 parent c83dea7 commit e43ebd0

File tree

8 files changed

+806
-0
lines changed

8 files changed

+806
-0
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace Mammatus.Attributes
5+
{
6+
/// <summary>
7+
/// Allows you to map a string as metadata to an enum item or property, like:
8+
///
9+
/// public enum TimeZoneId {
10+
/// [StringValue("Pacific Standard Time")]
11+
/// PST,
12+
/// . . .
13+
/// }
14+
///
15+
/// You can then use StringValueAttribute.Map to get a 2 way dictionary to get these string values out easily.
16+
///
17+
/// TODO: Add simpler methods that get just a simple value out one at a time instead of making you build an entire dictionary.
18+
/// </summary>
19+
public class StringValueAttribute : Attribute
20+
{
21+
protected string value;
22+
23+
public StringValueAttribute(string value)
24+
{
25+
this.value = value;
26+
}
27+
28+
public string Value
29+
{
30+
get
31+
{
32+
return value;
33+
}
34+
}
35+
36+
37+
// TODO: Could be public, but since enum isn't really a type we have no type safety here.
38+
protected static string GetStringValue(object enumValue)
39+
{
40+
var type = enumValue.GetType();
41+
var fieldInfo = type.GetField(enumValue.ToString());
42+
var stringValueAttributes = (StringValueAttribute[])fieldInfo.GetCustomAttributes(typeof(StringValueAttribute), false);
43+
if (stringValueAttributes.Length == 0)
44+
throw new ArgumentException("Enum value " + type.Name + "." + enumValue.ToString() + " does not have StringValueAttribute applied.");
45+
46+
return stringValueAttributes[0].value;
47+
}
48+
49+
/// <summary>
50+
/// Call this to map custom strings to enum values and back
51+
/// </summary>
52+
/// <typeparam name="T"></typeparam>
53+
/// <param name="enumToString">An empty Dictionary to be filled with a map of enum values to the string value in the attribute applied to them</param>
54+
/// <param name="stringToEnum">An empty dictionary to be filled with a map of attribute string values to their enum values</param>
55+
public static void Map<T>(Dictionary<T, string> enumToString, Dictionary<string, T> stringToEnum)
56+
{
57+
Type enumType = typeof(T);
58+
if (!enumType.IsEnum)
59+
throw new Exception("Type must be Enum.");
60+
61+
var enumValues = (T[])Enum.GetValues(enumType);
62+
foreach (var enumVal in enumValues)
63+
{
64+
string stringVal = GetStringValue(enumVal);
65+
enumToString.Add(enumVal, stringVal);
66+
stringToEnum.Add(stringVal, enumVal);
67+
}
68+
}
69+
}
70+
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
using System;
2+
3+
namespace Mammatus.Library.TimeZone
4+
{
5+
/// <summary>
6+
/// A DateTime object in UTC, and a TimeZoneInfo object representing what timezone it's meant to target.
7+
/// </summary>
8+
public class DateTimeAndZone
9+
{
10+
/// <summary>
11+
/// The time stored, represented in UTC.
12+
/// </summary>
13+
public DateTime Utc { get; set; }
14+
15+
/// <summary>
16+
/// The timezone this time originated from.
17+
/// </summary>
18+
public TimeZoneInfo TimeZone { get; set; }
19+
20+
21+
protected DateTimeAndZone(DateTime utc, TimeZoneInfo timeZone)
22+
{
23+
if (utc.Kind == DateTimeKind.Local)
24+
throw new ArgumentException("DateTime utc must be .Kind == DateTimeKind.Utc, not Local.", "utc");
25+
26+
Utc = utc;
27+
TimeZone = timeZone;
28+
}
29+
30+
public static DateTimeAndZone FromUtc(DateTime utc, TimeZoneInfo timeZone)
31+
{
32+
if (utc.Kind == DateTimeKind.Local)
33+
throw new ArgumentException("DateTime utc must be .Kind == DateTimeKind.Utc, not Local.", "utc");
34+
35+
return new DateTimeAndZone(utc, timeZone);
36+
}
37+
public static DateTimeAndZone FromUtcAndTimeZoneId(DateTime utc, TimeZoneId timeZoneId)
38+
{
39+
if (utc.Kind == DateTimeKind.Local)
40+
throw new ArgumentException("DateTime utc must be .Kind == DateTimeKind.Utc, not Local.", "utc");
41+
42+
return FromUtc(utc, TimeZoneIdMap.Current.FindSystemTimeZoneById(timeZoneId));
43+
}
44+
45+
/// <summary>
46+
/// Returns a DateTimeAndZone no matter what. If timezoneShortname is null or not a real timezone,
47+
/// it returns UTC
48+
/// </summary>
49+
/// <param name="utc"></param>
50+
/// <param name="timezoneShortname"></param>
51+
/// <returns></returns>
52+
public static DateTimeAndZone FromUtcAndShort(DateTime utc, string timezoneShortname)
53+
{
54+
if (utc.Kind == DateTimeKind.Local)
55+
throw new ArgumentException("DateTime utc must be .Kind == DateTimeKind.Utc, not Local.", "utc");
56+
57+
if (timezoneShortname == null)
58+
return new DateTimeAndZone(utc, TimeZoneInfo.Utc);
59+
60+
var tz = TimeZoneShortNameMap.Current.TimeZoneForShortName(timezoneShortname);
61+
62+
if (tz == null)
63+
return new DateTimeAndZone(utc, TimeZoneInfo.Utc);
64+
65+
return FromUtc(utc, tz);
66+
}
67+
68+
/// <summary>
69+
/// Throws an exception if timezone is bad.
70+
/// </summary>
71+
/// <param name="utc"></param>
72+
/// <param name="timezoneShortname"></param>
73+
/// <returns></returns>
74+
public static DateTimeAndZone FromUtcAndShortStrict(DateTime utc, string timezoneShortname)
75+
{
76+
if (utc.Kind == DateTimeKind.Local)
77+
throw new ArgumentException("DateTime utc must be .Kind == DateTimeKind.Utc, not Local.", "utc");
78+
79+
if (timezoneShortname == null)
80+
throw new ArgumentNullException("timezoneShortname");
81+
82+
var tz = TimeZoneShortNameMap.Current.TimeZoneForShortName(timezoneShortname);
83+
84+
if (tz == null)
85+
throw new ArgumentOutOfRangeException("timezoneShortname", timezoneShortname + " is not a mapped timezone.");
86+
87+
return FromUtc(utc, tz);
88+
}
89+
90+
public static DateTimeAndZone FromLocal(DateTime local, TimeZoneInfo timeZone)
91+
{
92+
if (local.Kind == DateTimeKind.Utc)
93+
throw new ArgumentException("DateTime utc must be .Kind == DateTimeKind.Unspecified, not Utc.", "local");
94+
95+
// We need to push this date from Local (default for Parse) to Unspecified because it's outside
96+
// the limited timezone space the DateTime structure understands (Local, UTC, and go fish)
97+
if (local.Kind == DateTimeKind.Local)
98+
local = DateTime.SpecifyKind(local, DateTimeKind.Unspecified);
99+
100+
var u = new DateTimeAndZone(TimeZoneInfo.ConvertTimeToUtc(local, timeZone), timeZone);
101+
return u;
102+
}
103+
public static DateTimeAndZone FromLocalAndTimeZoneId(DateTime local, TimeZoneId timeZoneId)
104+
{
105+
if (local.Kind == DateTimeKind.Utc)
106+
throw new ArgumentException("DateTime utc must be .Kind == DateTimeKind.Local, not Utc.", "local");
107+
108+
return FromLocal(local, TimeZoneIdMap.Current.FindSystemTimeZoneById(timeZoneId));
109+
}
110+
public static DateTimeAndZone FromLocalAndShort(DateTime local, string timezoneShortname)
111+
{
112+
if (local.Kind == DateTimeKind.Utc)
113+
throw new ArgumentException("DateTime utc must be .Kind == DateTimeKind.Local, not Utc.", "local");
114+
115+
return FromLocal(local, TimeZoneShortNameMap.Current.TimeZoneForShortName(timezoneShortname));
116+
}
117+
118+
119+
120+
/// <summary>
121+
/// Gets a DateTime object mapped to the current timezone.
122+
/// </summary>
123+
public DateTime Local
124+
{
125+
get
126+
{
127+
var local = TimeZoneInfo.ConvertTimeFromUtc(Utc, this.TimeZone);
128+
return local;
129+
}
130+
}
131+
132+
/// <summary>
133+
/// Whether this timezone is in daylight savings according to the TimeZoneInfo data.
134+
///
135+
/// Note that weirdness with the TimeZoneInfo object means this will sometimes return true for timezones that have
136+
/// no Daylight Savings rules; if you're trying to determine if a timezone has a DST version, it may be simpler to
137+
/// check whether it has a DST shortname mapped.
138+
/// </summary>
139+
public bool IsDaylightSavings
140+
{
141+
get
142+
{
143+
return this.TimeZone.IsDaylightSavingTime(Utc);
144+
}
145+
}
146+
147+
/// <summary>
148+
/// The short name for the timezone this date/timezone corresponds to, like PST, PDT, HKT.
149+
/// </summary>
150+
public string TimeZoneShortName
151+
{
152+
get
153+
{
154+
return TimeZoneShortNameMap.Current.ShortNameForTime(Utc, TimeZone);
155+
}
156+
}
157+
158+
159+
160+
/// <summary>
161+
/// Switches this object to another timezone.
162+
/// </summary>
163+
/// <param name="shortTz"></param>
164+
public void SwitchTimeZone(string shortTz)
165+
{
166+
SwitchTimeZone(TimeZoneShortNameMap.Current.TimeZoneForShortName(shortTz));
167+
}
168+
public void SwitchTimeZone(TimeZoneId timeZoneId)
169+
{
170+
SwitchTimeZone(TimeZoneIdMap.Current.FindSystemTimeZoneById(timeZoneId));
171+
}
172+
public void SwitchTimeZone(TimeZoneInfo timeZoneInfo)
173+
{
174+
TimeZone = timeZoneInfo;
175+
}
176+
177+
/// <summary>
178+
/// Gets a DateTime object for this time in the requested timezone.
179+
/// </summary>
180+
/// <param name="shortTz"></param>
181+
public DateTime ToLocal(string shortTz)
182+
{
183+
return ToLocal(TimeZoneShortNameMap.Current.TimeZoneForShortName(shortTz));
184+
}
185+
public DateTime ToLocal(TimeZoneId timeZoneId)
186+
{
187+
return ToLocal(TimeZoneIdMap.Current.FindSystemTimeZoneById(timeZoneId));
188+
}
189+
public DateTime ToLocal(TimeZoneInfo timeZoneInfo)
190+
{
191+
return TimeZoneInfo.ConvertTimeFromUtc(Utc, timeZoneInfo);
192+
}
193+
}
194+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using System;
2+
3+
namespace Mammatus.Library.TimeZone
4+
{
5+
public class DateTimeHelper
6+
{
7+
/// <summary>
8+
/// Parse the date using the Invariant (system) culture.
9+
///
10+
/// Since you're specifying a mask, the culture should be irrelevant.
11+
///
12+
/// Throws an exception if the parse isn't exact.
13+
/// </summary>
14+
/// <param name="s">Date string to parse</param>
15+
/// <param name="mask">Mask to use</param>
16+
/// <returns>DateTime parsed</returns>
17+
public static DateTime ParseExact(string s, string mask)
18+
{
19+
return DateTime.ParseExact(s, mask, System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AssumeUniversal);
20+
}
21+
22+
/// <summary>
23+
/// Returns the time between 2 dates formatted as h:mm:ss
24+
/// </summary>
25+
public static string ElapsedHMS(DateTime start, DateTime end)
26+
{
27+
var t = end - start;
28+
// Weirdo format required http://stackoverflow.com/questions/12543349/timespan-tostring-d-hhmm
29+
return t.ToString("h':'mm':'ss");
30+
}
31+
32+
/// <summary>
33+
/// Returns the time between 2 dates formatted as s.x (1 decimal place of partial seconds)
34+
/// </summary>
35+
public static string ElapsedSm(DateTime start, DateTime end)
36+
{
37+
var t = end - start;
38+
return t.TotalSeconds.ToString("0.0");
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)