5

The user enters a date and a time in separate textboxes. I then combine the date and time into a datetime. I need to convert this datetime to UTC to save it in the database. I have the user's time zone id saved in the database (they select it when they register). First, I tried the following:

string userTimeZoneID = "sometimezone"; // Retrieved from database
TimeZoneInfo userTimeZone = TimeZoneInfo.FindSystemTimeZoneById(userTimeZoneID);

DateTime dateOnly = someDate;
DateTime timeOnly = someTime;
DateTime combinedDateTime = dateOnly.Add(timeOnly.TimeOfDay);
DateTime convertedTime = TimeZoneInfo.ConvertTimeToUtc(combinedDateTime, userTimeZone);

This resulted in an exception:

The conversion could not be completed because the supplied DateTime did not have the Kind property set correctly. For example, when the Kind property is DateTimeKind.Local, the source time zone must be TimeZoneInfo.Local

I then tried setting the Kind property like so:

DateTime.SpecifyKind(combinedDateTime, DateTimeKind.Local);

This didn't work, so I tried:

DateTime.SpecifyKind(combinedDateTime, DateTimeKind.Unspecified);

This didn't work either. Can anyone explain what I need to do? Am I even going about this the correct way? Should I be using DateTimeOffset?

rikitikitik
  • 2,414
  • 2
  • 26
  • 37
HTX9
  • 1,717
  • 3
  • 15
  • 27
  • possible duplicate of [Convert local time to UTC in .NET framework 3.0](http://stackoverflow.com/questions/4668921/convert-local-time-to-utc-in-net-framework-3-0) – Shai Aug 12 '12 at 06:58
  • @Shai: Clearly not a duplicate, as that question has: "I must use in .NET framework 3.0, so can't use the TimeZoneInfo object." – Jon Skeet Aug 12 '12 at 07:02
  • @JonSkeet Ahhh didn't see that, might give the OP a lead though.. – Shai Aug 12 '12 at 07:19

2 Answers2

7

Just like all the other methods on DateTime, SpecifyKind doesn't change an existing value - it returns a new value. You need:

combinedDateTime = DateTime.SpecifyKind(combinedDateTime,
                                        DateTimeKind.Unspecified);

Personally I'd recommend using Noda Time which makes this kind of thing rather clearer in my rather biased view (I'm the main author). You'd end up with this code instead:

DateTimeZone zone = ...;
LocalDate date = ...;
LocalTime time = ...;
LocalDateTime combined = date + time;
ZonedDateTime zoned = combined.InZoneLeniently(zone);
// You can now get the "Instant", or convert to UTC, or whatever...

The "leniently" part is because when you convert local times to a specific zone, there's the possibility for the local value being invalid or ambiguous in the time zone due to DST changes.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thanks Jon! Can't believe it was that simple! I'll check out Noda Time because it looks like its much simpler to use! – HTX9 Aug 12 '12 at 07:22
  • @HTX9: Goodo - you may find that initially it actually feels more complicated, because it forces you to actually work out what kind of data you've got (local, dates vs times vs date/times), how to handle ambiguity etc. Those are things you should have been thinking about anyway, but the .NET API makes it harder to spot them. That's the theory, anyway :) – Jon Skeet Aug 12 '12 at 07:30
1

You can also try this

var combinedLocalTime = new DateTime((dateOnly + timeOnly.TimeOfDay).Ticks,DateTimeKind.Local);
var utcTime = combinedLocalTime.ToUniversalTime();
geekbytes0xff
  • 213
  • 2
  • 7