I am working on writing a managed wrapper around Massachusetts Bay Transportation Authority (MBTA) Realtime API. They have a API which returns the server time which is unix timestamp (epoch). The library under which I am implementing it is PCL Profile 78
which means I have limited support for BCL TimeZone, so I resorted to using Nodatime
I am trying to convert the time returned from server to Eastern Time which is America/New_York
as a DateTime
object and reverse way. My current code is very dirty
public static class TimeUtils
{
static readonly DateTimeZone mbtaTimeZone = DateTimeZoneProviders.Tzdb["America/New_York"];
static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public static DateTime GetMbtaDateTime (long unixTimestamp)
{
var mbtaEpochTime = epoch.AddSeconds (unixTimestamp);
var instant = Instant.FromUtc (mbtaEpochTime.Year, mbtaEpochTime.Month,
mbtaEpochTime.Day, mbtaEpochTime.Hour, mbtaEpochTime.Minute, mbtaEpochTime.Second);
var nodaTime = instant.InZone (mbtaTimeZone);
return nodaTime.ToDateTimeUnspecified ();
}
public static long MbtaDateTimeToUnixTimestamp (DateTime time)
{
TimeSpan secondsSinceEpochMbtaTz = time - epoch;
var instant = Instant.FromUtc (time.Year, time.Month, time.Day, time.Hour, time.Minute, time.Second);
var mbtaTzSpan = mbtaTimeZone.GetUtcOffset (instant).ToTimeSpan ();
var epochDiff = secondsSinceEpochMbtaTz - mbtaTzSpan;
return (long)epochDiff.TotalSeconds;
}
}
Is there another way to write this simply. I hope Nodatime should have support for converting an epoch time to America/New_York
DateTime and America/New_York
DateTime to epoch time. My method MbtaDateTimeToUnixTimestamp
is a brutal hack
Firstly, as mentioned in comments, it would be best to use Noda Time types throughout your code - only resort to DateTime
when you really have to. This should lead to significantly cleaner code throughout.
Converting a Unix timestamp to an Instant
is really easy:
Instant instant = Instant.FromUnixTimeSeconds(seconds);
You can then convert into a ZonedDateTime
as per your current code... and using ToDateTimeUnspecified
is fine if you really need to use DateTime
.
For the reverse, your current code looks broken to me - you're assuming the DateTime
is a UTC value, effectively. That would be at odds with your later use of the time zone. I suspect you want to convert the input to a LocalDateTime
, and then apply the time zone. For example:
public static long MbtaDateTimeToUnixTimestamp(DateTime time)
{
var local = LocalDateTime.FromDateTime(time);
var zoned = local.InZoneStrictly(mbtaTimeZone);
var instant = zoned.ToInstant();
return instant.Ticks / NodaConstants.TicksPerSecond;
}
Note the InZoneStrictly
call. This will throw an exception if either you pass in a local time which didn't exist or one that existed twice - in both cases due to DST transitions. This may well not be what you want - you really need to think about what you want to happen in those cases, or try to avoid them being feasible. See the time zones section of the documentation for more details and options.