i am using a function which is taking date time over the internet from external server.
here is the function which i am using to get date and time without depend on user pc date time settings.
using NodaTime;
using NodaTime.Text;
using System.IO;
using System.Globalization;
public static DateTime GetFastestNISTDate()
{
var result = DateTime.MinValue;
DateTime utcDateTime = DateTime.MinValue;
// Initialize the list of NIST time servers
// http://tf.nist.gov/tf-cgi/servers.cgi
string[] servers = new string[] {
"nist1-ny.ustiming.org",
"nist1-nj.ustiming.org",
"nist1-pa.ustiming.org",
"time-a.nist.gov",
"time-b.nist.gov",
"nist1.aol-va.symmetricom.com",
"nist1.columbiacountyga.gov",
"nist1-chi.ustiming.org",
"nist.expertsmi.com",
"nist.netservicesgroup.com"
};
// Try 5 servers in random order to spread the load
Random rnd = new Random();
foreach (string server in servers.OrderBy(s => rnd.NextDouble()).Take(5))
{
try
{
// Connect to the server (at port 13) and get the response
string serverResponse = string.Empty;
using (var reader = new StreamReader(new System.Net.Sockets.TcpClient(server, 13).GetStream()))
{
serverResponse = reader.ReadToEnd();
}
// If a response was received
if (!string.IsNullOrEmpty(serverResponse))
{
// Split the response string ("55596 11-02-14 13:54:11 00 0 0 478.1 UTC(NIST) *")
string[] tokens = serverResponse.Split(' ');
// Check the number of tokens
if (tokens.Length >= 6)
{
// Check the health status
string health = tokens[5];
if (health == "0")
{
// Get date and time parts from the server response
string[] dateParts = tokens[1].Split('-');
string[] timeParts = tokens[2].Split(':');
// Create a DateTime instance
utcDateTime = new DateTime(
Convert.ToInt32(dateParts[0]) + 2000,
Convert.ToInt32(dateParts[1]), Convert.ToInt32(dateParts[2]),
Convert.ToInt32(timeParts[0]), Convert.ToInt32(timeParts[1]),
Convert.ToInt32(timeParts[2]));
// Convert received (UTC) DateTime value to the local timezone
result = utcDateTime.ToLocalTime();
//return result;
return utcDateTime;
// Response successfully received; exit the loop
}
}
}
}
catch
{
// Ignore exception and try the next server
}
}
//return result;
return utcDateTime;
}
this variable result has local date time but i need to use Nodatime library where i will put my local date time variable result and also specify different timezone and Noda libraray will return local date and time of that timezone.
just guide me how to achieve it. i visit this url but still not clear how to incorporate Nodatime library and local time got from external server together to get another datetime based on different timezone.
looking for help with bit of sample code
thanks
EDIT
var wc = GetFastestNISTDate();
var pattern = InstantPattern.CreateWithInvariantCulture("dd/MM/yyyy HH:mm:ss");
var parseResult = pattern.Parse(wc.ToString("dd/MM/yyyy HH:mm:ss", CultureInfo.InvariantCulture));
if (!parseResult.Success)
throw new InvalidDataException("...whatever...");
var instant = parseResult.Value;
var timeZone = DateTimeZoneProviders.Tzdb["Europe/London"];
var zonedDateTime = instant.InZone(timeZone);
var bclDateTime = zonedDateTime.ToDateTimeUnspecified();
timezone translation is not working. i got the right date from this function GetFastestNISTDate(); and next i try to get local date and time of different timezone based on my first utc time but code return wrong time for London. i guess i am making mistake the code. can anyone see & help. thanks
EDIT 2
the samething i want to achieve by Nodatime library.
var wc = GetFastestNISTDate();
TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
DateTime cstTime = TimeZoneInfo.ConvertTimeFromUtc(wc, cstZone);
the above code is giving more or less right time. just tell me how to replace my last 2 line using nodatime library. thanks
Edit 3
var wc = GetFastestNISTDate();
Instant now = Instant.FromDateTimeUtc(wc);
var timeZone = DateTimeZoneProviders.Tzdb["Europe/London"];
var zonedDateTime = instant.InZone(timeZone);
var bclDateTime = zonedDateTime.ToDateTimeUnspecified();
#John just tell me the above code is ok because u said
Don't convert the UTC DateTime to a local version - it's pointless and confusing
Use Instant.FromDateTimeUtc to convert a UTC DateTime to an instant
GetFastestNISTDate() returning datetime instance and here we just create noda instance from utc datetime using like this code `Instant now = Instant.FromDateTimeUtc(wc);`
does it solve the issue.
EDIT 4
#Matt Johnson : thanks a lot for redirecting me to a good library.
i would definitely like to work with that library to achieve my task. before
use your library i have some question.
Point 1
what was wrong you notice in this routine GetFastestNISTDate(); the routine was query few NIST time servers and get the utctime.
utcDateTime = new DateTime(
Convert.ToInt32(dateParts[0]) + 2000,
Convert.ToInt32(dateParts[1]), Convert.ToInt32(dateParts[2]),
Convert.ToInt32(timeParts[0]), Convert.ToInt32(timeParts[1]),
Convert.ToInt32(timeParts[2]));
this routine GetFastestNISTDate(); was returning utcDateTime....was not a utc time ?
Point 2
when i was calling GetFastestNISTDate(); routine i notice some time this routine was returning DateTime.MinValue which was not expected result. i could understand why it was happening because NIST time servers was busy or blocked or timeout occured at that time.
Point 3
if i use your current code/library NodaTime.NetworkClock then i like to know which NTP server it will query by default?
if i use NodaTime.NetworkClock then is there any chance that some time i may get wrong date or null date due to NTP server is busy/block or timeout occur?
EDIT 5
var instant = NetworkClock.Instance.Now;
var timeZone = DateTimeZoneProviders.Tzdb["Europe/London"];
var zonedDateTime = instant.InZone(timeZone);
lbldate.Text = zonedDateTime.ToString("dd/MM/yyyy", CultureInfo.InvariantCulture);
lbltime.Text = zonedDateTime.ToString("hh:mm:ss", CultureInfo.InvariantCulture);
Your GetFastestNISTDate function uses the daytime protocol - which is essentially deprecated and NOT meant for machine interaction because its results are in no specific format. Even the docs from NIST strongly encourage users to use NTP instead of daytime.
You can find a simple C# implementation of an NTP client here.
To make things easier, I've implemented this client as a NodaTime.IClock. The code is on GitHub here. Simply install it from NuGet:
Install-Package NodaTime.NetworkClock
Then you can use it just like you would use the SystemClock:
var instant = NetworkClock.Instance.Now;
var timeZone = DateTimeZoneProviders.Tzdb["Europe/London"];
var zonedDateTime = instant.InZone(timeZone);
You shouldn't be converting the DateTime to a string and back - but your current problem is that you're converting the UTC value you get back from the server into a local DateTime for no obvious reason. Ideally, I'd suggest changing GetFastestNISTDate() to return an Instant, but assuming you can't do that:
Don't do the parsing yourself. Take the appropriate substring from the response, then use DateTime.ParseExact, specifying CultureInfo.InvariantCulture and DateTimeStyles.AssumeUniversal
Don't convert the UTC DateTime to a local version - it's pointless and confusing
Use Instant.FromDateTimeUtc to convert a UTC DateTime to an instant
The final part of your code (the last three lines) is okay, but why do you need a DateTime at all? If you can make as much of your code as possible use Noda Time, you'll get the greatest benefits in terms of code clarity.
Related
This code has been working for a long time now but has broken now when I try to pass DateTime.Now as the outageEndDate parameter:
public Outage(DateTime outageStartDate, DateTime outageEndDate, Dictionary<string, string> weeklyHours, string province, string localProvince)
{
this.outageStartDate = outageStartDate;
this.outageEndDate = outageEndDate;
this.weeklyHours = weeklyHours;
this.province = province;
localTime = TimeZoneInfo.FindSystemTimeZoneById(timeZones[localProvince]);
if (outageStartDate < outageEndDate)
{
TimeZoneInfo remoteTime = TimeZoneInfo.FindSystemTimeZoneById(timeZones[province]);
outageStartDate = TimeZoneInfo.ConvertTime(outageStartDate, localTime, remoteTime);
outageEndDate = TimeZoneInfo.ConvertTime(outageEndDate, localTime, remoteTime);
The error message I am getting on the last line is that the Kind property is not set correctly on the DateTime parameter (outageEndDate). I've Googled and checked SO for examples but I don't really understand the error message.
Any advice is appreciated.
Regards.
EDIT - The exact error message is:
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. Parameter name: sourceTimeZone
EDIT: outageEndDate.Kind = Utc
Thanks for clarifying your question.
If the DateTime instance Kind is Local, then TimeZoneInfo.ConvertTime will expect the second parameter to be the local timezone of your computer.
If DateTime instance Kind is Utc, then TimeZoneInfo.ConvertTime will expect the second parameter to be the Utc timezone.
You need to convert outageEndDate to the right timezone first, just in case the localProvice timezone doesn't match the timezone on your computer.
outageEndDate = TimeZoneInfo.ConvertTime(outageEndDate, localTime);
here is an example of something that you could try
It depends on what you mean by "a GMT + 1 timezone". Do you mean permanently UTC+1, or do you mean UTC+1 or UTC+2 depending on DST?
If you're using .NET 3.5, use TimeZoneInfo to get an appropriate time zone, then use:
// Store this statically somewhere
TimeZoneInfo maltaTimeZone = TimeZoneInfo.FindSystemTimeZoneById("...");
DateTime utc = DateTime.UtcNow;
DateTime malta = TimeZoneInfo.ConvertTimeFromUtc(utc, maltaTimeZone );
You'll need to work out the system ID for the Malta time zone, but you can do that easily by running this code locally:
Console.WriteLine(TimeZoneInfo.Local.Id);
If you're not using .NET 3.5, you'll need to work out the daylight savings yourself. To be honest, the easiest way to do that is going to be a simple lookup table. Work out the DST changes for the next few years, then write a simple method to return the offset at a particular UTC time with that list hardcoded. You might just want a sorted List<DateTime> with the known changes in, and alternate between 1 and 2 hours until your date is after the last change:
// Be very careful when building this list, and make sure they're UTC times!
private static readonly IEnumerable<DateTime> DstChanges = ...;
static DateTime ConvertToLocalTime(DateTime utc)
{
int hours = 1; // Or 2, depending on the first entry in your list
foreach (DateTime dstChange in DstChanges)
{
if (utc < dstChange)
{
return DateTime.SpecifyKind(utc.AddHours(hours), DateTimeKind.Local);
}
hours = 3 - hours; // Alternate between 1 and 2
}
throw new ArgumentOutOfRangeException("I don't have enough DST data!");
}
I want to be able to compare the current local time in C# using NodaTime to a fixed local time during the day. I do not need to worry about time zones or daylight savings, I just need to do the comparison with the local system time. So far I have this code...
IClock clock = SystemClock.Instance;
Instant instant = clock.Now;
var timeZone = DateTimeZoneProviders.Tzdb["Europe/London"];
var zonedDateTime = instant.InZone(timeZone);
var timeNow = zonedDateTime.ToString("HH:mm", System.Globalization.CultureInfo.InvariantCulture);
int tst = timeNow.CompareTo(new LocalTime(11, 00));
if (tst < 0)
{
eventLog1.WriteEntry("Time is before 11am.");
}
I get an error but as I am relatively new to C# and NodTime would appreciate some pointers where I am going wrong.
To get the local system time, you do need to worry about the time zone. You can use:
var clock = SystemClock.Instance; // Or inject it, preferrably
// Note that this *could* throw an exception. You could use
// DateTimeZoneProviders.Bcl.GetSystemDefault() to use the Windows
// time zone database.
var zone = DateTimeZoneProviders.Tzdb.GetSystemDefault();
var now = clock.Now.InZone(zone);
if (now.TimeOfDay < new LocalTime(11, 0))
{
...
}
In Noda Time 2.0, this is made somewhat simpler using ZonedClock:
var zonedClock = SystemClock.Instance.InTzdbSystemDefaultZone();
if (zonedClock.GetCurrentTimeOfDay() < new LocalTime(11, 0))
{
...
}
For "earlier than 11 o'clock" you could always use if (time.Hour < 11) of course, but using a LocalTime is more general.
i have to show date & time to user as per the user timezone id. i can not rely on user pc date time and setting. i was searching Google for the way and got a below code. code seems will query many external server for date & time but one thing not clear to me what kind of date & time i can expect from the below code.
some time i need india time. some time i need london time and some time i need other country local time. few county has many timezone that is why i want to send timezone id/name and want to get datetime based on timezone id/name.
so just guide me how to customize the below code where i can send user's side timezone id and routine will return right local time based on timezone id even if user pc datetime setting is wrong. looking for good help. thanks
public static DateTime GetFastestNISTDate()
{
var result = DateTime.MinValue;
// Initialize the list of NIST time servers
// http://tf.nist.gov/tf-cgi/servers.cgi
string[] servers = new string[] {
"nist1-ny.ustiming.org",
"nist1-nj.ustiming.org",
"nist1-pa.ustiming.org",
"time-a.nist.gov",
"time-b.nist.gov",
"nist1.aol-va.symmetricom.com",
"nist1.columbiacountyga.gov",
"nist1-chi.ustiming.org",
"nist.expertsmi.com",
"nist.netservicesgroup.com"
};
// Try 5 servers in random order to spread the load
Random rnd = new Random();
foreach (string server in servers.OrderBy(s => rnd.NextDouble()).Take(5))
{
try
{
// Connect to the server (at port 13) and get the response
string serverResponse = string.Empty;
using (var reader = new StreamReader(new System.Net.Sockets.TcpClient(server, 13).GetStream()))
{
serverResponse = reader.ReadToEnd();
}
// If a response was received
if (!string.IsNullOrEmpty(serverResponse))
{
// Split the response string ("55596 11-02-14 13:54:11 00 0 0 478.1 UTC(NIST) *")
string[] tokens = serverResponse.Split(' ');
// Check the number of tokens
if (tokens.Length >= 6)
{
// Check the health status
string health = tokens[5];
if (health == "0")
{
// Get date and time parts from the server response
string[] dateParts = tokens[1].Split('-');
string[] timeParts = tokens[2].Split(':');
// Create a DateTime instance
DateTime utcDateTime = new DateTime(
Convert.ToInt32(dateParts[0]) + 2000,
Convert.ToInt32(dateParts[1]), Convert.ToInt32(dateParts[2]),
Convert.ToInt32(timeParts[0]), Convert.ToInt32(timeParts[1]),
Convert.ToInt32(timeParts[2]));
// Convert received (UTC) DateTime value to the local timezone
result = utcDateTime.ToLocalTime();
return result;
// Response successfully received; exit the loop
}
}
}
}
catch
{
// Ignore exception and try the next server
}
}
return result;
}
EDIT
var wc = GetFastestNISTDate();
var pattern = InstantPattern.CreateWithInvariantCulture("dd/MM/yyyy HH:mm:ss");
var parseResult = pattern.Parse(wc.ToString("dd/MM/yyyy HH:mm:ss", CultureInfo.InvariantCulture));
if (!parseResult.Success)
throw new InvalidDataException("...whatever...");
var instant = parseResult.Value;
var timeZone = DateTimeZoneProviders.Tzdb["Europe/London"];
var zonedDateTime = instant.InZone(timeZone);
var bclDateTime = zonedDateTime.ToDateTimeUnspecified();
timezone translation is not working. i got the right date from this function GetFastestNISTDate(); and next i try to get local date and time of different timezone based on my first utc time but code return wrong time for London. i guess i am making mistake the code. can anyone see & help. thanks
DateTime tzDateTime = TimeZoneInfo.ConvertTimeFromUtc(utcDateTime, TimeZone);
Hope this helps.
The timeservers will report UTC time (that's why the variable is named utcDateTime).
The result = utcDateTime.ToLocalTime(); line converts that UTC time from the timeserver to the local time according to the timezone your computer is in. If you want a different timezone, you need to change that line.
As amit mentioned, you can use TimeZoneInfo.ConvertTimeFromUtc to convert to some specified timezone. See the example on that page.
TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
DateTime cstTime = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, cstZone);
Assume Instant variable contains "4/8/2014 11:09:24 AM" when i pass this value to this method it gives me an output "4/8/2014 12:09:24 PM" i checked in some time zone calcluator plus in oracle i should be getting "4/8/2014 04:09:24 PM" or "4/8/2014 16:09:24 PM" depending on hour format. why is it not converting into proper time format?
public static string ConvertDateTimeToUserTimeZone()
{
DateTime dt = DateTime.Parse("4/8/2014 11:09:24 AM");
Instant now = Instant.FromDateTimeUtc(DateTime.SpecifyKind(dt,DateTimeKind.Utc));
DateTimeZone dtZone = DateTimeZoneProviders.Tzdb["Europe/London"];
ZonedDateTime zdt = now.InZone(dtZone);
return zdt.ToDateTimeOffset().ToString("G");
}
Reading through your comments, it would appear that you'd like to do the following:
Parse the input string
Assign it to the the US Eastern time zone ("America/New_York")
Convert it to the time zone for the UK ("Europe/London")
Format it as a string in the general format
I will show using Noda Time for all of these steps, rather than mixing in non-Noda mechanisms.
var generalPattern = LocalDateTimePattern.CreateWithCurrentCulture("G");
string inputString = "4/8/2014 11:09:24 AM";
string sourceTimeZone = "America/New_York";
string targetTimeZone = "Europe/London";
LocalDateTime ldt1 = generalPattern.Parse(inputString).Value;
DateTimeZone tz1 = DateTimeZoneProviders.Tzdb[sourceTimeZone];
ZonedDateTime zdt1 = ldt1.InZoneLeniently(tz1);
DateTimeZone tz2 = DateTimeZoneProviders.Tzdb[targetTimeZone];
ZonedDateTime zdt2 = zdt1.WithZone(tz2);
LocalDateTime ldt2 = zdt2.LocalDateTime;
string output = generalPattern.Format(ldt2);
Notice that I used CreateWithCurrentCulture when setting up the pattern. This assumes you mean to use whatever the current culture is for the machine the code is running on. If that's not the case, then you should set a specific culture instead. This is important when you realize that users in the US will use M/D/Y formatting, while users in the UK will use D/M/Y formatting. This applies to both dates regardless of time zone. (In other words, 4/8/2014 could be April 8th, or August 4th).
Also notice that I used InZoneLeniently when applying the source time zone. This has lenient behavior when it comes to ambiguous and invalid input values due to DST transitions. If you want different behavior, then you might instead use InZoneStrictly, or use InZone and provide your own algorithm.
And finally, it should be noted that I inferred from your comments that you were sourcing these from the US Eastern time zone, which would be either EDT or EST depending on what time of year it is. If you actually meant that the values were always EDT, even when EST is the norm, then you would do the following:
DateTimeZone tz1 = DateTimeZone.ForOffset(Offset.FromHours(-4));
The value you got back from the method is in fact correct.
Let's break it down using LINQPad:
void Main()
{
var t = new DateTime(2014, 8, 4, 11, 09, 24, DateTimeKind.Utc);
var i = Instant.FromDateTimeUtc(t);
var s = ConvertDateTimeToUserTimeZone(i);
s.Dump("User time zone value");
}
public static string ConvertDateTimeToUserTimeZone(Instant now)
{
DateTimeZone dtZone = DateTimeZoneProviders.Tzdb["Europe/London"];
dtZone.GetUtcOffset(now).Dump("TZ at instant");
ZonedDateTime zdt = now.InZone(dtZone);
return zdt.ToDateTimeOffset().ToString("G");
}
Output:
TZ at instant
+01
User time zone value
04.08.2014 12:09:24
The time zone at the point of that instant is thus UTC+1, which means that the value you gave the code gives you the correct value back, 12:09.
To get 16:09, you would need UTC+5, and Europe/London has never been that value.
So 12:09:24 is correct, your assumptions that it should be 16:09:24 is wrong.
You need to go back to the code and/or sites you used to get the "correct" value, the source of your confusion is there, not in this code.
I have a very strange behavior on an application. The server is in New York(-5 GMT timezone) and the client - me, in Romania(+2 GMT timezone) so there is a 7 hour discrepancy. The problem I'm facing is when I try to save a date, let's say 12:00(day doesn't matter), the client is sending a request with the date 12:00, the 12:00 reaches the database but when it returns the severs returns the hour 19:00. I tried to debug on local to see who messes up the date but since I have the same date on server and on client there is no discrepancy.
This is the parameter sent to server &startDate=07/25/2012%2012:00:00
And this is the result: 1343232000000 - the seconds from the epoch(if you use a converter - http://www.epochconverter.com/ - you will see that the date is in fact Wed Jul 25 2012 19:00:00
here are some code snippets :
public static void GetProfessionalsHours(List<long> ids, out List<SalonProfessional> professionals)
{
professionals = new List<SalonProfessional>();
using (SqlConnection conn = new SqlConnection(DbConfig.ConnectionString))
{
using (
SqlCommand command = new SqlCommand("GetProfessionalsHours", conn) { CommandType = CommandType.StoredProcedure })
{
conn.Open();
command.Parameters.AddWithValue("professionalIDs", ids.CommaSeparated());
using (IDataReader reader = command.ExecuteReader())
{
//get normal schedule
while (reader.Read())
{
professionals.Add(SalonProfessional.GetSalonProfessional(reader));
}
reader.NextResult();
while (reader.Read())
{
professionals.Find(p => p.ID == reader.GetInt64(1)).Hours.Add(ProfessionalHours.GetProfessionalHour(reader));
}
//get overriden hours
reader.NextResult();
while (reader.Read())
{
professionals.Find(p => p.ID == reader.GetInt64(1)).OverriddenHours.Add(ProfessionalOverriddenHour.GetProfessionalOverriddenHour(reader));
}
}
}
}
}
public static ProfessionalOverriddenHour GetProfessionalOverriddenHour(IDataReader reader)
{
return new ProfessionalOverriddenHour()
{
ID = reader.GetInt64(0),
ProfessionalId = reader.GetInt64(1),
StartDate = reader.GetDateTime(2),
EndDate = reader.GetDateTime(3),
};
}
public JsonResult CalendarData(List<long> professionalIDs, CalendarData calendarData)
{
AjaxResponse response = new AjaxResponse();
response.Success = true;
CalendarDataResponseObject responseData = new CalendarDataResponseObject();
response.Content = responseData;
try
{
List<SalonProfessional> professionals = null;
CalendarOperations.GetProfessionalsHours(professionalIDs, out professionals);
responseData.Professionals = professionals;
}
catch (Exception ex)
{
response.Success = true;
response.ErrorMessage = "Could not retrieve calendar data";
ExceptionsOperations.LogException(ex, "Could not retrieve calendar data");
}
return Json(response, JsonRequestBehavior.AllowGet);
}
The problem is on StartDate and EndDate from the ProfessionalOverriddenHour object.
You could send the offset (z) or agree on UTC
Console.WriteLine(DateTime.Now.ToString("hh mm ss z"));
Console.WriteLine(DateTime.Now.ToUniversalTime().ToString());
Try storing and retrieving date string after converting with reference to UTC first.
Here is a excerpt from Scott Hanselman blog post
Phrased differently, don't use DateTime.Now for ANY date calculations
or to store anything. Use DateTime.UTCNow and be aware that some
methods will freak out if you send them future dates, as they should.
Avoid doing ANYTHING in local time until that last second when you
show the DateTime to the user.
Also check out Daylight saving time and time zone best practices
Like already suggested, always use UTC when storing dates (in databases, files, etc.), and let the client itself decide on what timezone should be used when displaying that date (and only then!)
You can of course get it working by just including the timezone data in the date string, but it makes everything more complex (imho), stick to the simple case where dates are always UTC, unless you're displaying it to the users.
Thanks a lot for help guys!
The problem was that both the server and the client were converting the date to their local time. So the solution was to get the hours difference on server and add them to the date and on javascript to work with utc time. Basically when you don't need time zones, work with UTC everywhere.