How Convert UTC Date & time to local time using di

2019-01-27 07:19发布

问题:

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.
  1. 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 ?

  1. 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.

  1. 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);

回答1:

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);


回答2:

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.