Setting datetime to a specific date by specific way - c#

I have a variable with date time which I have to set on a specific date by these rukles and scenarios:
The API that I connect to has a daily limit and once that limit is reached I have to wait until NEXT DAY until 9:10 AM CEST <= This is very important
So I've been simply doing this:
var localTime = TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time"));
var tomorrowAt0910 = localTime.AddDays(1).Date + new TimeSpan(9, 10, 0);
And I have realized that this code has a bug, because I can have following scenarios:
Let's say my application would expire on 30th of July at 15:00 PM in which case this logic up there would be VALID
BUT
We have a next following scenario which is more likely to happen:
Application expires on 31st of July 5:00 AM, in which case this logic is faulty because RENEWAL DATE will be set to 1st of August 9:10AM WHICH IS BAD
If the application expires in this second case, I should set the date to same day and few hours difference (from 5AM to 9AM)
How could I do this?

It sounds like what you really want is to say:
Find the current time in Central Europe
Find 9:10am on the same date
If 9:10am is after the current time, add a day
So something like:
// No need to do this more than once
private static readonly TimeZoneInfo centralEuropeZone =
TimeZoneInfo.FindSystemTimeZoneById("Central European Standard Time")
private static DateTime GetUtcResetTime()
{
// Using UtcNow to make it clear that the system time zone is irrelevant
var centralEuropeNow = TimeZoneInfo.ConvertTime(DateTime.UtcNow, centralEuropeZone);
var centralEuropeResetTime = centralEuropeNow.Date + new TimeSpan(9, 10, 0);
if (centralEuropeResetTime <= centralEuropeNow)
{
centralEuropeResetTime = centralEuropeResetTime.AddDays(1);
}
return TimeZoneInfo.ConvertTimeToUtc(centralEuropeResetTime, centralEuropeZone);
}
I've made that return a UTC DateTime so that no other code needs to worry about which zone it's in.

Check if the expire date is less that the current date, if so add one day.
DateTime expireDate = new DateTime(2018, 7, 30, 22, 0, 0); //for testing
DateTime tomorrowAt0910 = DateTime.Now.Date.AddHours(9).AddMinutes(10);
if (expireDate.Date < DateTime.Now.Date)
{
tomorrowAt0910.AddDays(1);
}

Related

How to check if an hour exists in a specific Time Zone in .NET

During a Daylight Saving Time transition, the clock is moved forward, and so a specific hour will not exist in that specific day for that specific time zone.
Is there an easy way in .NET to find out if an hour exists or not for a time zone?
The only way I found was by trying to convert an hour to UTC, and check for an exception:
public bool IsValidTime(DateTime date, TimeZoneInfo tzi)
{
try
{
date = DateTime.SpecifyKind(date, DateTimeKind.Unspecified);
TimeZoneInfo.ConvertTimeToUtc(date, tzi);
return true;
}
catch
{
return false;
}
}
And so running something like this will return false:
var date = new DateTime(2020, 3, 8);
var tzi = TimeZoneInfo.FindSystemTimeZoneById("Cuba Standard Time");
var isValid = IsValidTime(date, tzi);
Is there any built in way of doing this, that is less messy?
You can use IsInvalidTime method of TimeZoneInfo.
From Microsoft : https://learn.microsoft.com/en-us/dotnet/api/system.timezoneinfo.isinvalidtime?view=netframework-4.6.2
Example: In the Pacific Time zone, daylight saving time begins at 2:00 A.M. on April 2, 2006. The following code passes the time at one-minute intervals from 1:59 A.M. on April 2, 2006, to 3:01 A.M. on April 2, 2006, to the IsInvalidTime method of a TimeZoneInfo object that represents the Pacific Time zone. The console output indicates that all times from 2:00 A.M. on April 2, 2006, to 2:59 A.M. on April 2, 2006, are invalid.
// Specify DateTimeKind in Date constructor
DateTime baseTime = new DateTime(2007, 3, 11, 1, 59, 0, DateTimeKind.Unspecified);
DateTime newTime;
// Get Pacific Standard Time zone
TimeZoneInfo pstZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");
// List possible invalid times for a 63-minute interval, from 1:59 AM to 3:01 AM
for (int ctr = 0; ctr < 63; ctr++)
{
// Because of assignment, newTime.Kind is also DateTimeKind.Unspecified
newTime = baseTime.AddMinutes(ctr);
Console.WriteLine("{0} is invalid: {1}", newTime, pstZone.IsInvalidTime(newTime));
}

How to determine if now(utc) is within the range of the given days of the week and times of the day in ISO 8601 format

I run into this question of how to determine if DateTime.UtcNow (e.g. 2018-01-01T20:00:00Z) falls within the given range of days and times that are in another timezone. There are no specific dates given, just the days of the week, and the time of the day. The given time is in ISO 8601 standard format.
To simplify this question, it can be how to check if a UTC time is within business hours in China.
For example, the given day and time range is given by someone form China in time zone +08:00, it can be: FromDayOfWeek = "Friday", FromTimeOfDay = "17:00:00+8:00", ToDayOfWeek = "Monday", ToTimeOfWeek = "08:00:00+8:00". I need to determine if "now" in China is sometime between the given range (Friday 17:00:00+8:00 - Monday 08:00:00+8:00).
I'm stuck at how to convert the DateTime and get the day of the week in that local time, since 2018-01-01T20:00:00Z is Monday in UK, but at the same time, since China is +08:00, it is already Tuesday in China.
My approach:
// parse the time to get the zone first (+08:00)
TimeSpan ts = TimeSpan.Parse("-08:00");
// Create a custom time zone since the time zone id is not given, and cannot be searched by SearchTimeZoneById
TimeZoneInfo tzi = TimeZoneInfo.CreateCustomTimeZone(zoneId, ts, displayName, standardName);
DateTime localDateTime = TimeZoneInfo.ConvertTime(Date.UtcNow, tzi);
String localDay = localDateTime.DayOfWeek;
// Determine if localDay is between FromDayOfWeek and ToDayOfWeek
// cast the days to integers from 1 (Monday) to 7 (Sunday)
// create an array of days in integar days = [5, 6, 7, 1]
// if days.contains(localDays), check the times
...
Can anyone suggest some better solutions? I am not sure if mine works, and there are holes in how to deal with Day Light Saving time, since the zone will change, and how to check the time range. I am new to C#, any suggestions of libraries I can use is great!
Instead of converting both start and end times from UTC, just convert the other time into UTC
TimeZoneInfo chinaTimeZone = TimeZoneInfo.CreateCustomTimeZone(zoneID, TimeSpan.Parse("-08:00"), displayName, standardName);
DateTime FromTime = new DateTime(2018, 0, 19, 13, 0, 0); // year, month, day, hour, minute, second : Friday 1pm
DateTime ToTime = new DateTime(2018, 0, 21, 1, 0, 0); // year, month, day, hour, minute, second : Monday 1am
DateTime nowinUTC = DateTime.UtcNow;
DateTime nowInChina = TimeZoneInfo.ConvertTimeFromUtc(nowinUTC, chinaTimeZone);
if(FromTime< nowInChina && ToTime> nowInChina)
{
// Time is within the from and two times
}
Per the comments on #Moffen's answer, you only want to check if Now is within a specific DayOfWeek range:
public void CheckAll(List<SomeClass> spans)
{
var chinaTZ = TimeZoneInfo.CreateCustomTimeZone(zoneID, TimeSpan.Parse("-08:00"), displayName, standardName);
var nowInChina = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, chinaTZ);
foreach ( var span in spans )
{
if (InRange(nowInChina, span.startDay, span.endDay))
// Do something on success
// Check for valid times here
;
else
// Do something on Failure
;
}
}
public bool InRange(DateTime dateToCheck, DayOfWeek startDay, DayOfWeek endDay)
{
// Initialise as one day prior because first action in loop is to increment current
var current = (int)startDay - 1;
do
{
// Move to next day, wrap back to Sunday if went past Saturday
current = (current + 1) % 7;
if (dateToCheck.DayOfWeek == (DayOfWeek)current)
return true;
} while (current != (int)endDay);
return false;
}

Are adding weeks, months or years to a date/time independent from time zone?

My software displays date/time using local time and then send it to server in UTC. On the server-side I want to add months, years, weeks, days etc to this date/time. However, the question is, if I use such methods with UTC date/time and then convert it back to local time, would the result be always the same, as if I use this methods with local time directly?
This is an example in C#:
// #1
var utc = DateTime.Now.ToUtcTime();
utc = utc.AddWeeks(2); // or AddDays, AddYears, AddMonths...
var localtime = utc.ToLocalTime();
// #2
var localtime = DateTime.Now;
localtime = localtime.AddWeeks(2); // or AddDays, AddYears, AddMonths...
Would the results in #1 and #2 always be the same? Or timezone can influence the result?
The answer may surprise you but it is NO. You cannot add days, weeks, months, or years to a UTC timestamp, convert it to a local time zone, and expect to have the same result as if you had added directly to the local time.
The reason is that not all local days have 24 hours. Depending on the time zone, the rules for that zone, and whether DST is transitioning in the period in question, some "days" may have 23, 23.5, 24, 24.5 or 25 hours. (If you are trying to be precise, then instead use the term "standard days" to indicate you mean exactly 24 hours.)
As an example, first set your computer to one of the USA time zones that changes for DST, such as Pacific Time or Eastern Time. Then run these examples:
This one covers the 2013 "spring-forward" transition:
DateTime local1 = new DateTime(2013, 3, 10, 0, 0, 0, DateTimeKind.Local);
DateTime local2 = local1.AddDays(1);
DateTime utc1 = local1.ToUniversalTime();
DateTime utc2 = utc1.AddDays(1);
DateTime local3 = utc2.ToLocalTime();
Debug.WriteLine(local2); // 3/11/2013 12:00:00 AM
Debug.WriteLine(local3); // 3/11/2013 1:00:00 AM
And this one covers the 2013 "fall-back" transition:
DateTime local1 = new DateTime(2013, 11, 3, 0, 0, 0, DateTimeKind.Local);
DateTime local2 = local1.AddDays(1);
DateTime utc1 = local1.ToUniversalTime();
DateTime utc2 = utc1.AddDays(1);
DateTime local3 = utc2.ToLocalTime();
Debug.WriteLine(local2); // 11/4/2013 12:00:00 AM
Debug.WriteLine(local3); // 11/3/2013 11:00:00 PM
As you can see in both examples - the result was an hour off, one direction or the other.
A couple of other points:
There is no AddWeeks method. Multiply by 7 and add days instead.
There is no ToUtcTime method. I think you were looking for ToUniversalTime.
Don't call DateTime.Now.ToUniversalTime(). That is redundant since inside .Now it has to take the UTC time and convert to local time anyway. Instead, use DateTime.UtcNow.
If this code is running on a server, you shouldn't be calling .Now or .ToLocalTime or ever working with DateTime that has a Local kind. If you do, then you are introducing the time zone of the server - not of the user. If your users are not in the same time zone, or if you ever deploy your application somewhere else, you will have problems.
If you want to avoid these kind of problems, then look into NodaTime. It's API will prevent you from making common mistakes.
Here's what you should be doing instead:
// on the client
DateTime local = new DateTime(2013, 3, 10, 0, 0, 0, DateTimeKind.Local);
DateTime utc = local.ToUniversalTime();
string zoneId = TimeZoneInfo.Local.Id;
// send both utc time and zone to the server
// ...
// on the server
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById(zoneId);
DateTime theirTime = TimeZoneInfo.ConvertTimeFromUtc(utc, tzi);
DateTime newDate = theirTime.AddDays(1);
Debug.WriteLine(newDate); // 3/11/2013 12:00:00 AM
And just for good measure, here is how it would look if you used Noda Time instead:
// on the client
LocalDateTime local = new LocalDateTime(2013, 3, 10, 0, 0, 0);
DateTimeZone zone = DateTimeZoneProviders.Tzdb.GetSystemDefault();
ZonedDateTime zdt = local.InZoneStrictly(zone);
// send zdt to server
// ...
// on the server
LocalDateTime newDate = zdt.LocalDateTime.PlusDays(1);
Debug.WriteLine(newDate); // 3/11/2013 12:00:00 AM

How to set time to midnight for current day?

Every time that I create a non-nullable datetime in my mvc3 application it defaults to now(), where now is current date with current time. I would like to default it to today's date with 12am as the time.
I'm trying to default the time in my mvc...but...the following isn't setting to todays date #12am. Instead it defaults to now with current date and time.
private DateTime _Begin = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 12, 0, 0);
public DateTime Begin { get { return _Begin; } set { _Begin = value; } }
How can I set to 12am for the current date for non-nullable datetime?
You can use the Date property of the DateTime object - eg
DateTime midnight = DateTime.Now.Date;
So your code example becomes
private DateTime _Begin = DateTime.Now.Date;
public DateTime Begin { get { return _Begin; } set { _Begin = value; } }
PS. going back to your original code setting the hours to 12 will give you time of noon for the current day, so instead you could have used 0...
var now = DateTime.Now;
new DateTime(now.Year, now.Month, now.Day, 0, 0, 0);
I believe you are looking for DateTime.Today. The documentation states:
An object that is set to today's date, with the time component set to 00:00:00.
http://msdn.microsoft.com/en-us/library/system.datetime.today.aspx
Your code would be
DateTime _Begin = DateTime.Today;
Using some of the above recommendations, the following function and code is working for search a date range:
Set date with the time component set to 00:00:00
public static DateTime GetDateZeroTime(DateTime date)
{
return new DateTime(date.Year, date.Month, date.Day, 0, 0, 0);
}
Usage
var modifieddatebegin = Tools.Utilities.GetDateZeroTime(form.modifieddatebegin);
var modifieddateend = Tools.Utilities.GetDateZeroTime(form.modifieddateend.AddDays(1));
Only need to set it to
DateTime.Now.Date
Console.WriteLine(DateTime.Now.Date.ToString("yyyy-MM-dd HH:mm:ss"));
Console.Read();
It shows
"2017-04-08 00:00:00"
on my machine.
Related, so I thought I would post for others. If you want to find the UTC of the start of today (for your timezone) the following code works for any UTC offset (-23.5 thru +23.5). This looks like we add X hours then subtract X hours, but the important thing is the ".Date" after the add.
double utcOffset= 10.0; // Set to your UTC offset in hours (eg. Melbourne Australia)
var now = DateTime.UtcNow;
var startOfToday = now.AddHours(utcOffset - 24.0).Date;
startOfToday = startOfToday.AddHours(24.0 - utcOffset);
Most of the suggested solutions can cause a 1 day error depending on the time associated with each date. If you are looking for an integer number of calendar days between to dates, regardless of the time associated with each date, I have found that this works well:
return (dateOne.Value.Date - dateTwo.Value.Date).Days;
Try this:
DateTime Date = DateTime.Now.AddHours(-DateTime.Now.Hour).AddMinutes(-DateTime.Now.Minute)
.AddSeconds(-DateTime.Now.Second);
Output will be like:
07/29/2015 00:00:00

How to represent the current UK time?

I'm facing an issue while converting dates between my server and client where both is running in Germany. The Regional settings on the client machines could be set to both UK or Germany.I recieve a date from the server which is CET format, and I need to represent this time on UI as UK time. For example a time recieved from server like say, 01/07/2010 01:00:00 should be represented on the UI as 01/07/2010 00:00:00. I have written a converter for this purpose, however while running it 'am getting a time difference of 2 hours.Below is the code, please can you help?
public class LocalToGmtConverter : IDateConverter
{
private readonly TimeZoneInfo timeZoneInfo;
public LocalToGmtConverter()
: this(TimeZoneInfo.Local)
{
}
public LocalToGmtConverter(TimeZoneInfo timeZoneInfo)
{
this.timeZoneInfo = timeZoneInfo;
}
public DateTime Convert(DateTime localDate)
{
var utcKind = DateTime.SpecifyKind(localDate, DateTimeKind.Utc);
return utcKind;
}
public DateTime ConvertBack(object fromServer)
{
DateTime serverDate = (DateTime)fromServer;
var utcOffset = timeZoneInfo.GetUtcOffset(serverDate);
var uiTime = serverDate- utcOffset;
return uiTime;
}
}
I think you're converting to UTC (instead of UK) time. Since there is still summer time in Central Europe (event if the temperatures say otherwise), the difference is +2 hours until October, 31st.
If you know that you're converting from Germany to UK (i.e. CEST to BST in summer and CET to GMT in winter), why you don't just subtract 1 hour?
If you want the time zone information for UK, you can construct it using
var britishZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
Then you could convert the date using
var newDate = TimeZoneInfo.ConvertTime(serverDate, TimeZoneInfo.Local, britishZone);
This is what I do:
var BritishZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
DateTime dt = DateTime.SpecifyKind(DateTime.UtcNow, DateTimeKind.Unspecified);
DateTime DateTimeInBritishLocal = TimeZoneInfo.ConvertTime(dt, TimeZoneInfo.Utc, BritishZone);
I needed to add the SpecifyKind part or the ConvertTime throws an exception
Use TimeZoneInfo.ConvertTime to convert original input timezone (CET) to target timezone (UK).
public static DateTime ConvertTime(
DateTime dateTime,
TimeZoneInfo sourceTimeZone,
TimeZoneInfo destinationTimeZone
)
Full guidance on MSDN here:
Converting Times Between Time Zones
Modified code sample from MSDN:
DateTime ceTime = new DateTime(2007, 02, 01, 08, 00, 00);
try
{
TimeZoneInfo ceZone = TimeZoneInfo.FindSystemTimeZoneById("Central Europe Standard Time");
TimeZoneInfo gmtZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
Console.WriteLine("{0} {1} is {2} GMT time.",
ceTime,
ceZone.IsDaylightSavingTime(ceTime) ? ceZone.DaylightName : ceZone.StandardName,
TimeZoneInfo.ConvertTime(ceTime, ceZone, gmtZone));
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("The registry does not define the required timezones.");
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("Registry data on the required timezones has been corrupted.");
}
The better approach to deal with local times is store them in unified representation such as UTC.
So you can convert all input times to UTC (via .ToUniversalTime()), and store (or transmit) its value. When you need to show it just convert back by using .ToLocalTime().
So you avoid rquirements to know which time zone was original value and can easily show stored value in different timezones.
Also you can avoid incoming troubles where you have to write specific logic for processing time in next timezone trying to figure out how to convert them amongs all available.
I realize the question is for C#, but if all you want to do is a single conversion you can do this from the PowerShell command line:
$d = [DateTime]::Parse("04/02/2014 17:00:00")
$gmt = [TimeZoneInfo]::FindSystemTimeZoneById("GMT Standard Time");
[TimeZoneInfo]::ConvertTime($d, $gmt, [TimeZoneInfo]::Local)
This script would convert 17:00 UK time into your local time zone.
For me, that would be CST. It's interesting to note that if I had set the date to 03/27/2014, the result would be different because the UK daylight saving time kicks in on different dates that the US.

Categories