Convert datetime entered by user to UTC - c#

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?

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.

You can also try this
var combinedLocalTime = new DateTime((dateOnly + timeOnly.TimeOfDay).Ticks,DateTimeKind.Local);
var utcTime = combinedLocalTime.ToUniversalTime();

Related

JavaScriptSerializer :After serialization Date becomes less by one day [duplicate]

I am using JavaScriptSerializer for serializing DateTime, but when I deserialize it show one day less from the date it get serialize:
Here is test:
DateTime startDate=new DateTime(2012,1,20);//set the 20th of January
JavaScriptSerializer serializer=new JavaScriptSerializer();
string serializeDate= serializer.Serialize(startDate);
DateTime afterDeserialize= serializer.Deserialize<DateTime>(serializeDate);//I get 19th of Jan
Assert.Equals(startDate, afterDeserialize);
firstly I thougt it because of javascript datetime format but as I know for javascript Month is zero index 0=January, but I am getting one day less than the original date.
It's not losing a day arbitrarily, it's converting to a UTC date (or I should say using the date in a UTC date format) so when it's unserialized it you're no longer within your personal time zone. It's basically performing:
DateTime whateverDate = /* incoming date */;
long ticks = whateverDate.ToUniversalTime() // make UTC
.Subtract(new DateTime(1970, 1, 1)) // subtract UNIX Epoch
.TotalMilliseconds(); // get milliseconds since then
// push in to the "\/Date(ticks)\/" format
String value = String.Format(#"\/Date({0})\/", ticks);
However, try the following:
// or you rely on it serializing, then bring it back to your own local time
// (apply the time zone).
afterDeserialize = afterDeserialize.ToLocalTime();
You'll now have the UTC time back to your local time (with time zone applied).
To Pass your test:
DateTime startDate = new DateTime(2012,1,20);
JavaScriptSerializer serializer = new JavaScriptSerializer();
String serializeDate = serializer.Serialize(startDate);
DateTime afterDeserialize = serializer.Deserialize<DateTime>(serializeDate)
.ToLocalTime(); // Note: this is added
Assert.Equals(startDate, afterDeserialize); // pass!
I had the same problem and solved it by using
Newtonsoft.Json.JsonConvert.SerializeObject()
instead of
new System.Web.Script.Serialization.JavaScriptSerializer().Serialize().
The latter call stores your DateTime converted to some random timezone (GMT+0 seems to be hardcoded).
On deserializing JavaScriptSerializer giving me output in UTC (Universal Time) which due to change in hours change the date. As Brad Christie suggested to change DateTime to UTC it can solve the problems.
But actually there is no need to change the:
DateTime startDate = new DateTime(2012, 1, 20).ToUniversalTime();
as it is already taking it as Universal Time. So I just convert the output of deserialize to LocalTime:
DateTime afterDeserialize= serializer.Deserialize<DateTime>(serializeDate);
afterDeserialize.ToLocalTime();
it solved the issue.

C# Converting string to DateTime without using DateTimeOffset

I'm trying to bring over facebook events to my website. They will be coming from different regions. For example, an event is in Eastern Time (-4 hours difference from UTC) and my local time is Central time (-5 hours difference from UTC).
I am calling their graph API from a console app. I get the date of the events like this:
// get event items
me = fbClient.Get(url);
var startTime = me["start_time"];
var endTime = me["end_time"];
the start time shows: "2017-04-30T13:00:00-0400" object {string}
When I try to convert that string into a DateColumn type, it changes the output time to:
var dateTime = Convert.ToDateTime(startTime);
{4/30/2017 12:00:00 PM}
It shifted the hour from 13 -> 12, how do I convert the string into date using DateTime and not using DateTimeoffset?
This examples shows how to do it using DateTimeOffset, but I need mine in DateTime type?
https://stackoverflow.com/a/19403747/1019042
You can use the DateTime property of DateTimeOffset like the answer accepted in the link you've provided.
Or, if you really like to do it just with DateTime you can if you cut the timezone from the string:
var dt = DateTime.ParseExact(startTime.Substring(0,19),
"yyyy-MM-ddTHH:mm:ss",
CultureInfo.InvariantCulture);

Nodatime create a ZonedDateTime given a time and timezone

Can anyone give me the most straightforward way to create a ZonedDateTime, given "4:30pm" and "America/Chicago".
I want this object to represent that time for the current date in that timezone.
Thanks!
I tried this... but it seems to actually give me an instant in the local timezone which gets offset when creating the zonedDateTime.
string time = "4:30pm";
string timezone = "America/Chicago";
DateTime dateTime;
if (DateTime.TryParse(time, out dateTime))
{
var instant = new Instant(dateTime.Ticks);
DateTimeZone tz = DateTimeZoneProviders.Tzdb[timezone];
var zonedDateTime = instant.InZone(tz);
using NodaTime;
using NodaTime.Text;
// your inputs
string time = "4:30pm";
string timezone = "America/Chicago";
// parse the time string using Noda Time's pattern API
LocalTimePattern pattern = LocalTimePattern.CreateWithCurrentCulture("h:mmtt");
ParseResult<LocalTime> parseResult = pattern.Parse(time);
if (!parseResult.Success) {
// handle parse failure
}
LocalTime localTime = parseResult.Value;
// get the current date in the target time zone
DateTimeZone tz = DateTimeZoneProviders.Tzdb[timezone];
IClock clock = SystemClock.Instance;
Instant now = clock.Now;
LocalDate today = now.InZone(tz).Date;
// combine the date and time
LocalDateTime ldt = today.At(localTime);
// bind it to the time zone
ZonedDateTime result = ldt.InZoneLeniently(tz);
A few notes:
I intentionally separated many items into separate variables so you could see the progression from one type to the next. You may condense them as desired for fewer lines of code. I also used the explicit type names. Feel free to use var.
You may want to put this in a function. When you do, you should pass in the clock variable as a parameter. This will let you replace the system clock for a FakeClock in your unit tests.
Be sure to understand how InZoneLeniently behaves, and note how it's changing in the upcoming 2.0 release. See "Lenient resolver changes" in the 2.x migration guide.

Get Current DateTime C# without hour

Hi I currently have a TimePicker. It returns an object TimeSpan.
What I need to do is to set a DateTimeOffset that is equal to current date plus the TimeSpan from the TimePicker.
How can I actually get the current DateTimeOffset.now that doesn't have a Time on it, only the Date so that I can add the offset to it.
Thanks
As in DateTime object you have a Date property, it returns date part without time (it means time is 00:00:00).
DateTime today = DateTimeOffset.Now.Date;
DateTime result = today + yourTimeSpan;
With this solution will lost Offset information (because Date is a DateTime). To keep it you just need to subtract time part:
DateTimeOffset now = DateTimeOffset.Now;
DateTimeOffset result = now - now.Time + yourTimeSpan;
Or with constructor:
DateTimeOffset now = DateTimeOffset.Now;
DateTimeOffset result = new DateTimeOffset(now.Date + yourTimeSpan, now.Offset);
Can you not just .Date it?
var a = DateTimeOffset.Now.Date;
try using:
DateTime.Today
instead of Now.

Calculating start of day and start of month in UTC

I have users that can be in different timezones and I'm looking to determine the UTC value of the beginning of their days and their months. Inside an object, I have a method that attempts to do that; it looks like this:
private void SetUserStartTimesUTC()
{
DateTime TheNow = DateTime.UtcNow.ConvertUTCTimeToUserTime(this.UserTimezoneID);
DateTime TheUserStartDateUserTime = TheNow.Date;
DateTime TheUserStartMonthUserTime = new DateTime(TheNow.Year, TheNow.Month, 1);
DateTime TheUserEndMonthUserTime = TheUserStartMonthUserTime.AddMonths(1);
this.UserStartOfDayUTC = TheUserStartDateUserTime.ConvertUserTimeToUTCTime(this.UserTimezoneID);
this.UserStartOfMonthUTC = TheUserStartMonthUserTime.ConvertUserTimeToUTCTime(this.UserTimezoneID);
this.UserEndOfMonthUTC = TheUserEndMonthUserTime.ConvertUserTimeToUTCTime(this.UserTimezoneID);
}
And this method depends on two other extension methods that do the conversions between a user's time and UTC time
public static DateTime ConvertUserTimeToUTCTime(this DateTime TheUserTime, string TheTimezoneID)
{
TimeZoneInfo TheTZ = TimeZoneInfo.FindSystemTimeZoneById(TheTimezoneID);
DateTime TheUTCTime = new DateTime();
if (TheTZ != null)
{
DateTime UserTime = new DateTime(TheUserTime.Year, TheUserTime.Month, TheUserTime.Day, TheUserTime.Hour, TheUserTime.Minute, TheUserTime.Second);
TheUTCTime = TimeZoneInfo.ConvertTimeToUtc(UserTime, TheTZ);
}
return TheUTCTime;
}
public static DateTime ConvertUTCTimeToUserTime(this DateTime TheUTCTime, string TheTimezoneID)
{
TimeZoneInfo TheTZ = TimeZoneInfo.FindSystemTimeZoneById(TheTimezoneID);
DateTime TheUserTime = new DateTime();
if (TheTZ != null)
{
DateTime UTCTime = new DateTime(TheUTCTime.Year, TheUTCTime.Month, TheUTCTime.Day, TheUTCTime.Hour, TheUTCTime.Minute, 0, DateTimeKind.Utc);
TheUserTime = TimeZoneInfo.ConvertTime(UTCTime, TheTZ);
}
return TheUserTime;
}
Now I've been dealing with timezone issues for a while and I know that timezone issues can introduce off-by-one bugs that can be hard to detect.
Does my implementation of timezones seem to be correct or is there an edge case that will create some sort of off-by-one bug?
Thanks for your suggestions.
Your methods seem needlessly complicated, to be honest.
Why would you have a parameter called TheUTCTime and then create a UTC version of it? Shouldn't it already have a Kind of UTC? Even if it didn't, you would be better off using DateTime.SpecifyKind - currently when converting one way you wipe out the seconds, whereas converting the other way you don't... in both cases you wipe out any sub-second values.
Also:
TimeZoneInfo.FindSystemTimeZoneById never returns null
Returning new DateTime() (i.e. January 1st 0001 AD) if the time zone can't be found seems like a really poor way of indicating an error
There's no need to have a local variable in your conversion methods; just return the result of calling ConvertTime directly
Your "end of month" is really "start of the next month"; that may be what you want, but it's not clear.
Personally I would strongly advise you to avoid the BCL DateTime for all of this entirely. I'm entirely biased being the main author, but I'd at least hope that you'd find Noda Time more pleasant to work with... it separates out the idea of "date with no time component", "time with no date component", "local date and time with no specific time zone" and "date and time in a particular time zone"... so the type system helps you to only do sensible things.
EDIT: If you really have to do this within the BCL types, I'd write it like this:
private void SetUserStartTimesUTC()
{
DateTime nowUtc = DateTime.UtcNow;
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(UserTimeZoneID);
// User-local values, all with a Kind of Unspecified.
DateTime now = TimeZoneInfo.ConvertTime(nowUtc, zone);
DateTime today = now.Date;
DateTime startOfThisMonth = todayUser.AddDays(1 - today.Day);
DateTime startOfNextMonth = startOfThisMonth.AddMonths(1);
// Now convert back to UTC... see note below
UserStartOfDayUTC = TimeZoneInfo.ConvertTimeToUtc(today, zone);
UserStartOfMonthUTC = TimeZoneInfo.ConvertTimeToUtc(startOfThisMonth, zone);
UserEndOfMonthUTC = TimeZoneInfo.ConvertTimeToUtc(startOfNextMonth, zone);
}
The extension methods you've added really don't provide much benefit, as you can see.
Now, the code mentions a "note" - you're currently always assuming that midnight always exists and is unambiguous. That's not true in all time zones. For example, in Brazil, on daylight saving changes forward, the time skips from midnight to 1am - so midnight itself is invalid, basically.
In Noda Time we fix this by having DateTimeZone.AtStartOfDay(LocalDate) but it's not as easy with the BCL.
For comparison, the equivalent Noda Time code would look like this:
private void SetUserStartTimesUTC()
{
// clock would be a dependency; you *could* use SystemClock.Instance.Now,
// but the code would be much more testable if you injected it.
Instant now = clock.Now;
// You can choose to use TZDB or the BCL time zones
DateTimeZone zone = zoneProvider.FindSystemTimeZoneById(UserTimeZoneID);
LocalDateTime userLocalNow = now.InZone(zone);
LocalDate today = userLocalNow.Date;
LocalDate startOfThisMonth = today.PlusDays(1 - today.Day);
LocalDate startOfNextMonth = startOfThisMonth.PlusMonths(1);
UserStartOfDayUTC = zone.AtStartOfDay(today);
UserStartOfMonthUTC = zone.AtStartOfDay(startOfThisMonth);
UserEndOfMonthUTC = zone.AtStartOfDay(startOfNextMonth);
}
... where the properties would be of type ZonedDateTime (which remembers the time zone). You could change them to be of type Instant (which is just a point in time) if you want, just chaining a ToInstant call for each property setter.

Categories

Resources