0

How can I get datetime with timezone info (GMT/UTC +7)?

Let's say in real time it was 06:00 PM and then someone alters the local PC time to 01:00 PM. How do I get datetime 06:00 PM?

I have tried this:

System.Globalization.CultureInfo.CurrentCulture.ClearCachedData();
DateTime utcTime = DateTime.UtcNow;
TimeZoneInfo tzi = TimeZoneInfo.FindSystemTimeZoneById("SE Asia Standard Time");
DateTime localTime = TimeZoneInfo.ConvertTimeFromUtc(utcTime, tzi);
Console.WriteLine(localTime);

But I still got 01:00 PM.

chopperfield
  • 559
  • 1
  • 7
  • 25
  • sorry for typo, edited – chopperfield Oct 19 '17 at 11:10
  • In your question I am confused by what you mean by your times. When you say 6pm do you mean 6pm UTC? (and same for 1pm) _I am also confused how a +7 offset alters the time by 5 hours._ – mjwills Oct 19 '17 at 11:17
  • I am little confused what do you want to achieve but if you really want correct time try to read it from NTP server instead of system datetime class. Try this: https://stackoverflow.com/questions/1193955/how-to-query-an-ntp-server-using-c – MartinS Oct 19 '17 at 11:18
  • in real time means nows time, but let's say someone/jerk alternating the local pc time e.g `02.00`pm or maybe `03.00` pm. because in the other project iam saving data record time by using `datetime.now`. of course i would like to save real time (06.00), not `02.00` or `03.00`. sorry for confusing – chopperfield Oct 19 '17 at 11:18
  • I think @MartinŠevic has provided the answer, if I've understood correctly. – spodger Oct 19 '17 at 11:20
  • @dba well, it's not. its just about saving record datetime.now to database – chopperfield Oct 19 '17 at 11:20
  • `because in the other project iam saving data record time` If you are saving to the database, you should get the **database** to generate the relevant DateTime (not the client). – mjwills Oct 19 '17 at 11:20
  • okay, there was an overlapping of our comments :) So the system Time is compromised, I guess, your best shot would be to get it from NTP as @MartinŠevic suggests... – dba Oct 19 '17 at 11:22
  • What's the database engine? – MartinS Oct 19 '17 at 11:23
  • if the DB runs on the same client, then it will get the same erroneous time, won't it? – dba Oct 19 '17 at 11:24
  • If so, @chopperfield haven't specified engine yet. If it is on remote server then just add GETDATE() equivalent to query. If DB is on local machine, contact NTP server. – MartinS Oct 19 '17 at 11:26
  • @MartinŠevic iam using `sql server`. and also i need to get sql server time to make prompting. so i thougth it would be better i declare a new datetime by using server timezone to matching it – chopperfield Oct 19 '17 at 11:28
  • such as `if (datetime.today > 10/19/2017) { insert() }` then do insert command. the main condition is this `datetime.today` because its getting local pc time (can be alter) – chopperfield Oct 19 '17 at 11:30
  • Yeah, got it, then just contact NTP server. It will give you +0 time. Then just add hours to time by desired timezone. – MartinS Oct 19 '17 at 11:32
  • @MartinŠevic for insert command already using `GETDATE()`. the main concern is the `if condition`. i will check it. Thanks man ~ – chopperfield Oct 19 '17 at 11:36
  • 1
    Honestly, why would you have your machine check the validity of (or even alter) the system time? This really shouldn't be the task of a non-OS program. If time sync across the infrastructure is a requirement for your app, state it in the pre-requisites of the software and let the administrator/OS deal with the details. Also an admin might chose to have an onsite NTP server over what you define. – Markus Deibel Oct 19 '17 at 11:39
  • @MartinŠevic - Time zones are much more complicated than can be accounted for by just adding hours. See "time zone != offset" in [the timezone tag wiki](https://stackoverflow.com/tags/timezone/info). – Matt Johnson-Pint Oct 19 '17 at 18:56

2 Answers2

3

Just contact NTP server and take it's time. Code found on stackoverflow. You need internet connection to do that of course.

public static DateTime GetNetworkTime() {
    //default Windows time server
    const string ntpServer = "time.windows.com";

    // NTP message size - 16 bytes of the digest (RFC 2030)
    var ntpData = new byte[48];

    //Setting the Leap Indicator, Version Number and Mode values
    ntpData[0] = 0x1B; //LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)

    var addresses = Dns.GetHostEntry(ntpServer).AddressList;

    //The UDP port number assigned to NTP is 123
    var ipEndPoint = new IPEndPoint(addresses[0], 123);
    //NTP uses UDP

    using(var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
    {
        socket.Connect(ipEndPoint);

        //Stops code hang if NTP is blocked
        socket.ReceiveTimeout = 3000;     

        socket.Send(ntpData);
        socket.Receive(ntpData);
        socket.Close();
    }

    //Offset to get to the "Transmit Timestamp" field (time at which the reply 
    //departed the server for the client, in 64-bit timestamp format."
    const byte serverReplyTime = 40;

    //Get the seconds part
    ulong intPart = BitConverter.ToUInt32(ntpData, serverReplyTime);

    //Get the seconds fraction
    ulong fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4);

    //Convert From big-endian to little-endian
    intPart = SwapEndianness(intPart);
    fractPart = SwapEndianness(fractPart);

    var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);

    //**UTC** time
    var networkDateTime = (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds((long)milliseconds);

    return networkDateTime.ToLocalTime(); }

// stackoverflow.com/a/3294698/162671 static uint SwapEndianness(ulong x) {
    return (uint) (((x & 0x000000ff) << 24) +
                   ((x & 0x0000ff00) << 8) +
                   ((x & 0x00ff0000) >> 8) +
                   ((x & 0xff000000) >> 24)); }
dba
  • 1,159
  • 1
  • 14
  • 22
MartinS
  • 751
  • 3
  • 12
  • 27
1

If you don't want to trust the information on the user's PC at all, then you will need:

  1. An alternate source of time zone data
  2. A network time source, such as via NTP

You can achieve the first with Noda Time, and the second with my NodaTime.NetworkClock add-on. Internally, it uses similar NTP code as that given by Martin's answer.

public static DateTime GetRealTimeInZone(string timeZoneId)
{
    var clock = NetworkClock.Instance;
    var now = clock.GetCurrentInstant();
    var tz = DateTimeZoneProviders.Tzdb[timeZoneId];
    return now.InZone(tz).ToDateTimeUnspecified();
}

Usage:

DateTime dt = GetRealTimeInZone("Asia/Bangkok");

Or, if you do trust the local time zone setting, just not the clock, then:

public static DateTime GetRealTimeInZone()
{
    var clock = NetworkClock.Instance;
    var now = clock.GetCurrentInstant();
    var tz = DateTimeZoneProviders.Tzdb.GetSystemDefault();
    return now.InZone(tz).ToDateTimeUnspecified();
}

Usage:

DateTime dt = GetRealTimeInZone();
Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • on NetworkClock.Instance, the `instance` was red line (error) it says `object doesn't contain definition for instance` because on networkclock doenst have instance(). how do i solve this? – chopperfield Oct 20 '17 at 02:57
  • iam just importing `using NodaTime; using NodaTime.Extensions;` – chopperfield Oct 20 '17 at 03:07
  • C# is case sensitive. – Matt Johnson-Pint Oct 20 '17 at 03:14
  • am i wrong, just by copy - paste your code ?. because the problem is. what i said earlier in the comment section. – chopperfield Oct 20 '17 at 03:29
  • In your comment you said `instance` with a lower-case `i`. It should be `Instance` with an upper-case `I`. If you copy-pasted my code, all you should need is `using NodaTime;` at the top, and of course to import both `NodaTime` and `NodaTime.NetworkClock` from Nuget. – Matt Johnson-Pint Oct 20 '17 at 03:51
  • oh that misunderstood, iam using upper case `I`. for installing nugget iam using this `PM> Install-Package NodaTime`. but it seems i can't find imporint `Nodatime.NetworkClock` – chopperfield Oct 20 '17 at 04:05
  • They are separate packages. You have to install them both. – Matt Johnson-Pint Oct 20 '17 at 04:12
  • It's a separate extension library. https://github.com/mj1856/NodaTime.NetworkClock – Matt Johnson-Pint Oct 20 '17 at 04:16
  • thanks it works perfectly. i want to ask other question, about `("Asia/Bangkok")` is it different by using time zone name `https://msdn.microsoft.com/en-us/library/gg154758.aspx`. and also how do i use local time zone (read local pc time zone, let's say some dummies altering the time but not the pc local timezone. so i don't need to specify it manually like ("Asia/Bangkok") because let's say there's pc on Singapore and Japan ) – chopperfield Oct 20 '17 at 05:02
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/157113/discussion-between-matt-johnson-and-chopperfield). – Matt Johnson-Pint Oct 20 '17 at 05:03