I have CSV data files with timestamped records that are in local time. Unfortunately the data files cover the period where daylight saving time changes (Nov 3rd 2013) so the time component of the timestamps for the records go: 12:45, 1:00, 1:15, 1:30, 1:45, 1:00, 1:15, 1:30, 1:45, 2:00
. I want to be able to convert and store the values in the database as UTC.
Unfortunately the standard DateTime.Parse()
function of .NET will parse as this (all November 3rd 2013):
| Time String | Parsed Local Time | In DST | Parsed Local Time to UTC
| 12:45 am | 12:45 am | Yes | 4:45 am
| 12:59:59 am | 12:59:59 am | Yes | 4:59:59 am
| 01:00 am | 1:00 am | No | 6:00 am
| 01:15 am | 1:15 am | No | 6:15 am
So it never sees the 1:00-1:59:59 am
range as being in DST and my parsed timestamps in UTC jumps an hour.
Is there a library or class out there that will allow me to parse the timestamps and take into account the change in DST? Like some instantiatable class that will remember the stream of timestamps it's already received and adjust the parsed timestamp accordingly?
Assumptions about the data that can be made while parsing:
yyyy/mm/dd HH:mm:ss
(2013/11/03 00:45:00
)Note: While my software is in C#, I did not tag C#/.NET specifically as I figured I could use any language's implementation of a solution and recode if necessary.
In C#:
// Define the input values.
string[] input =
{
"2013-11-03 00:45:00",
"2013-11-03 01:00:00",
"2013-11-03 01:15:00",
"2013-11-03 01:30:00",
"2013-11-03 01:45:00",
"2013-11-03 01:00:00",
"2013-11-03 01:15:00",
"2013-11-03 01:30:00",
"2013-11-03 01:45:00",
"2013-11-03 02:00:00",
};
// Get the time zone the input is meant to be interpreted in.
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
// Create an array for the output values
DateTimeOffset[] output = new DateTimeOffset[input.Length];
// Start with the assumption that DST is active, as ambiguities occur when moving
// out of daylight time into standard time.
bool dst = true;
// Iterate through the input.
for (int i = 0; i < input.Length; i++)
{
// Parse the input string as a DateTime with Unspecified kind
DateTime dt = DateTime.ParseExact(input[i], "yyyy-MM-dd HH:mm:ss",
CultureInfo.InvariantCulture);
// Determine the offset.
TimeSpan offset;
if (tz.IsAmbiguousTime(dt))
{
// Get the possible offsets, and use the DST flag and the previous entry
// to determine if we are past the transition point. This only works
// because we have outside knowledge that the items are in sequence.
TimeSpan[] offsets = tz.GetAmbiguousTimeOffsets(dt);
offset = dst && (i == 0 || dt >= output[i - 1].DateTime)
? offsets[1] : offsets[0];
}
else
{
// The value is unambiguous, so just get the single offset it can be.
offset = tz.GetUtcOffset(dt);
}
// Use the determined values to construct a DateTimeOffset
DateTimeOffset dto = new DateTimeOffset(dt, offset);
// We can unambiguously check a DateTimeOffset for daylight saving time,
// which sets up the DST flag for the next iteration.
dst = tz.IsDaylightSavingTime(dto);
// Save the DateTimeOffset to the output array.
output[i] = dto;
}
// Show the output for debugging
foreach (var dto in output)
{
Console.WriteLine("{0:yyyy-MM-dd HH:mm:ss zzzz} => {1:yyyy-MM-dd HH:mm:ss} UTC",
dto, dto.UtcDateTime);
}
Output:
2013-11-03 00:45:00 -04:00 => 2013-11-03 04:45:00 UTC
2013-11-03 01:00:00 -04:00 => 2013-11-03 05:00:00 UTC
2013-11-03 01:15:00 -04:00 => 2013-11-03 05:15:00 UTC
2013-11-03 01:30:00 -04:00 => 2013-11-03 05:30:00 UTC
2013-11-03 01:45:00 -04:00 => 2013-11-03 05:45:00 UTC
2013-11-03 01:00:00 -05:00 => 2013-11-03 06:00:00 UTC
2013-11-03 01:15:00 -05:00 => 2013-11-03 06:15:00 UTC
2013-11-03 01:30:00 -05:00 => 2013-11-03 06:30:00 UTC
2013-11-03 01:45:00 -05:00 => 2013-11-03 06:45:00 UTC
2013-11-03 02:00:00 -05:00 => 2013-11-03 07:00:00 UTC
Note that this assumes that the first time you encounter an ambiguous time like 1:00 that it will be in DST. Say your list was truncated to just the last 5 entries - you wouldn't know that those were in standard time. There's not much you could do in that particular case.