2

Attached is a method I am currently using that takes in a list of DateTime strings, their input format (i.e. yyyy-MM-dd HH:mm:ss), and their offset in the form of hours.

As for the culture and "standard", I am using InvariantCulture and I am converting the times to UTC.

    public int unixFormat3(string dateTimeInput, string inputFormat, int hours)
    {
        DateTime result;
        CultureInfo provider = CultureInfo.InvariantCulture;
        result = DateTime.ParseExact(dateTimeInput, inputFormat, provider);

        int unixTime = (Int32)(result.ToUniversalTime().AddHours(hours).Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc))).TotalSeconds;
        return unixTime;
    }

Two issues with said method:

  1. I am using this website as a comparison. If my input is 2014-03-18 21:00:00, my output, according to my method, is 1395190800, which converts back to 2014-03-19 01:00:00. It has a four hour difference. The desired output is this:

enter image description here

  1. If my input is 2014-03-18 24:00:00, I get this error:

The DateTime represented by the string is not supported in calendar System.Globalization.GregorianCalendar.

Noticeably, it does not allow the input of 24 in the HH part. This is a weird error as NodaTime handles it just fine... Though that's irrelevant as I am using DateTime.

Does anyone have any insight on this area?

EDIT:

Upon some experimentation, removing the .ToUniversalTime() removes my 4-hour offset.. Why is this happening?

public int unixFormat3(string dateTimeInput, string inputFormat, int hours)
{
    DateTime result;
    CultureInfo provider = CultureInfo.InvariantCulture;
    result = DateTime.ParseExact(dateTimeInput, inputFormat, provider);

    int unixTime = (Int32)(result.AddHours(hours).Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc))).TotalSeconds;
    return unixTime;
}
theGreenCabbage
  • 5,197
  • 19
  • 79
  • 169

2 Answers2

1

This document, http://www.w3.org/TR/NOTE-datetime, cited in this question How to know whether a given string is a valid UTC DateTime format? does not list 24 as a valid hour value.

This document, http://en.wikipedia.org/wiki/Iso8601, cited by an answer to the question does list 24:00 as a valid time. This one, http://en.wikipedia.org/wiki/12-hour_clock#Confusion_at_noon_and_midnight, also says 24:00 is valid.

Community
  • 1
  • 1
bf2020
  • 742
  • 4
  • 7
1

The System.DateTime object represents hours as an integer value between 0 and 23 (see http://msdn.microsoft.com/en-us/library/vstudio/system.datetime.hour(v=vs.100).aspx). As far as I know, NodaTime doesn't use any of the .NET provided DateTime or DateTimeOffset classes and handles everything itself, which is why it's handling an hour of 24 correctly.

As for why ToUniversalTime() is adding an offset, its probably because the ParseExact is returning a date that's already been adjusted. (What is the value of result just before you call ToUniversalTime()?)

You may also want to change your call to use this overload of ParseExact instead:

result = DateTime.ParseExact(dateTimeInput, inputFormat, provider, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);

This tells the parser to assume the time is in UTC if no time zone is specified in the parsed string.

As a side note, you should probably declare your Unix epoch as a readonly global variable somewhere and use TryParseExact instead of ParseExact.

public class UnixTime
{
    public static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);

    public int unixFormat3(string dateTimeInput, string inputFormat, int hours)
    {
        int unixTime = -1;
        DateTime result = DateTime.MinValue;
        if (DateTime.TryParseExact(dateTimeInput, inputFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out result))
        {
            unixTime = (int)(result.AddHours(hours).Subtract(UnixTime.Epoch)).TotalSeconds;
        }

        return unixTime;
    }
}
Scott Dorman
  • 42,236
  • 12
  • 79
  • 110
  • Thank you SO much for the detailed answer. It was extremely informative. Could you give me a suggestion on what to do with C# not handling 24? That just seems so strange; I can imagine many scenarios where there would be a 24 as input. – theGreenCabbage Mar 24 '14 at 19:11
  • With `DateTimeStyles.AssumeUniversal` http://puu.sh/7HUMD/2f48f7e43d.png. Without `ToUniversalTime()` or `DateTimeStyles.AssumeUniversal`: http://puu.sh/7HUUv/a6cf577df3.png It appears that when you don't assume/to UTC, the code doesn't offset for you. When you do assume UTC, it offsets for you. How exactly does it assume my offset? According to my computer and my timezone, my timezone is: `UTC -05:00`, not `-04:00`. – theGreenCabbage Mar 24 '14 at 19:18
  • Wow. The more I learn about DateTimes and timezones, the more confused I am. Since the input is `2014-03-18 21:00:00`, it's timezone agnostic. It's ambiguous what timezone it is, because there's no locale or timezone tags. Do you assume that's UTC by default? – theGreenCabbage Mar 24 '14 at 19:38
  • 1
    You should have `DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal`. Otherwise, the input is treated as universal, but it is then converted to *local*. – Matt Johnson-Pint Mar 24 '14 at 22:51
  • 1
    @MattJohnson, I updated the code to include `DateTimeStyles.AdjustToUniversal'. Thanks! – Scott Dorman Mar 25 '14 at 17:36
  • @theGreenCabbage You may be able to use the `DateTime.TryParseExact` method to see if it will parse into a date first. (In fact, you probably should be doing that anyway.) That will at least keep you from throwing an exception. It won't solve the 24 hour problem, so you'll probably have to do some parsing yourself if you want to handle that. As for the hour difference in time zone offsets, you may be hitting a daylight savings time issue. Under the hood, it's looking at a lot of different things to figure out your time zone offset, but a lot of it is based on the culture and TimeZoneInfo used. – Scott Dorman Mar 25 '14 at 17:52