Strange output while using DateTime

Hello all, as I have been working on a timer which shows how long is left for something to finish, eg for an event. It was perfectly fine yesterday, showing correct time amounts, after testing it again today, currenDate = DateTime.Now is actually using the date from yesterday when I wrote the code in my opinion, here is the code

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;
using TMPro;
using System.Globalization;

public class BrawlPassTimer : MonoBehaviour
{
    [SerializeField] DateTime currentDate;
    [SerializeField] DateTime startDate;
    [SerializeField] DateTime endDate;
    public TMP_Text FinishTimeText;
    void Start()
    {
        // Setting timer start and end dates

        currentDate = DateTime.Now; // Checking current date, time etc
        Debug.Log(currentDate); // Checking if it actually shows current date
        startDate = new DateTime(2021, 5, 23, 10, 0, 0); // Set the start date
        Debug.Log(startDate); // Checking if it actually sets the start date
        endDate = new DateTime(2021, 5, 24, 10, 0, 0); // Set the end date
        Debug.Log(endDate); // Checking if it actually sets the end date
        TimeSpan timeDifference = endDate - currentDate; // Seeing what is the time difference
        string time = new DateTime(timeDifference.Ticks).ToString("dd:hh:mm:ss");
        Debug.Log(time);
        FinishTimeText.text = time;
    }
}

It is current;y exactly 10am where I am, I set the start date as the 23rd May, starting at 10am which is right now, and set the end date exactly one day after, but it debug’s 01:11:59:41, which is 1 day, 11 hours, 59 min and 41 sec, what is the exact issue? It’s really confusing me, if I change the end date from 24th May to 23rd may, it gives me the error for ArumentOutOfRangeException, which is correct because the time has already passed. So why does it add more time than it should if the time difference is only 1 day?

https://docs.microsoft.com/en-us/dotnet/api/system.datetime.now?view=net-5.0

It’s local time. The DateTime constructor is unspecified time (which in reality means UTC since you didn’t give it timezone).

I’m guessing you’re somewhere East, New Zealand or similar timezone?

Try (takes your local time and subtracts 24 hours)
endDate = DateTime.Now.Subtract(TimeSpan.FromDays(1));

Although I’m not sure what you’re testing here exactly. :slight_smile:

Hey Lurking, oh yes that makes sense now, and I’m located in AU, UTC+10, I’ll try out what you just given me above. Also one little question, I don’t understand why it’s affecting me today, didn’t bother me yesterday. Also after just checking this currentDate = DateTime.Now;. I used Debug.Log to see what the time is and the time given is literally where I am right now:eyes::eyes::eyes:

Okay, just think about it in context. You will be storing the value of DateTime.Now somewhere. Then after a while you will compare it to a later DateTime.Now. So what are you doing right now with this thing above? Maybe there is a better way to do it if you can explain what you’re trying to do. :slight_smile:

Alr, explaining what I’m doing a bit clearly. I’m making a pass, where you claim rewards etc. I want to make a timer which shows how the long the pass is going to be on for. Just like events, showing you when the event starts, then when it starts it shows how long there is left until the even is over! Hope this makes more sense:)

You should be careful with the time zones. If you say the event will start at 1pm on 30th of May and will end at 2pm on 1st of June, that means different timespan for people in different time zones.
First, you need to decide if you will use local time or a fixed time zone. Because you can say, “the event will start at 1pm on 30th of May, GMT”, this is one moment for everyone, regardless which time zone the users are in.
This will determinate if you need to calculate with time zones at the first place.

If you want to use time zones (so everyone’s event will start 1pm on 30th of May local time), then you will need to adjust your DateTime, you should use the DateTimeKind.Local parameter.

Thanks for that Lurking, really made me more clear about what I need to do. In my opinion for now, I would just do event starts lets say 1pm GMT, Not using local time. You mentioned to use DateTimeKind.Local to use for people in their timezone. Does the parameter automatically find people’s timezone? But going back to my original question, is the problem really because of the timezones or something else?

You’re using DateTime wrong ^^. The first thing you have to realise is what those two types actually represent: DateTime and TimeSpan.

A DateTime value always represents an absolute point in time that is composed of a date and a time value (hence the name DateTime).

A TimeSpan value always represents a relative time interval.

While DateTime values are subject to timezones, daylight saving time, and other regional oddities, TimeSpan values just represent a relative timespan. When you subtract to DateTime values from each other, the DateTime struct will take care of timezones and what not for you (if you specified them correctly, of course). The result will be a TimeSpan that actually represents how much relative time has passed between those two points.

So your issue is essentially this line:

string time = new DateTime(timeDifference.Ticks).ToString("dd:hh:mm:ss");

Why do you construct a new absolute DateTime value here? To quote the docs:

So converting a ticks value that represents a relative time span into a DateTime value, you essentially are referencing some day in the year 1. After that you just print out the days and time portion of that time stamp.

You should just get rid of that DateTime in that line all together. Try

string time = timeDifference.ToString("dd:hh:mm:ss");

instead.

In theory it doesn’t really matter if you use local time or UTC. However if the timestamps could be kept for a longer time period, people could move between timezones and things would not add up properly. That’s why most servers use UTC when it comes to timestamps so it’s the same for all around the world. If you need to display a DateTime value to the local user, you can always convert it into local time. Many websites often do this with JavaScript inside the browser.

I can recommend to watch this Computerphile video on timezones. Note that code that others have already written are the DateTime and TimeSpan structs, so just use them correctly ^^.

Hey Bunny, really informative message just there, really like to thank you for your effort, just to start off, here is my updated script which is working correctly now (already did it before your post).

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;
using TMPro;
using System.Globalization;

public class BrawlPassTimer : MonoBehaviour
{
    [SerializeField] DateTime currentDate;
    [SerializeField] DateTime startDate;
    [SerializeField] DateTime endDate;
    public TMP_Text FinishTimeText;
    void Start()
    {
        // Setting timer start and end dates
        TimeZoneInfo localZone = TimeZoneInfo.Local;
        localZone.IsDaylightSavingTime(DateTime.Now);
        Debug.Log(localZone);
        currentDate = DateTime.Now; // Checking current date, time etc
        Debug.Log(currentDate); // Checking if it actually shows current date
        startDate = new DateTime(2021, 5, 23, 10, 0, 0, DateTimeKind.Local); // Set the start date
        Debug.Log(startDate); // Checking if it actually sets the start date
        endDate = new DateTime(2021, 5, 24, 10, 0, 0, DateTimeKind.Local); // Set the end date
        Debug.Log(endDate); // Checking if it actually sets the end date
        TimeSpan timeDifference = endDate - currentDate; // Seeing what is the time difference
        Debug.Log(timeDifference.Days.ToString() + "d " + timeDifference.Hours.ToString() + "h");
        FinishTimeText.text = timeDifference.Days.ToString() + "d " + timeDifference.Hours.ToString() + "h";
    }

    //void Update()
    //{
    //    foreach (TimeZoneInfo i in TimeZoneInfo.GetSystemTimeZones())
    //    {
    //        Debug.Log(i.Id);
    //    }
    //}
}

I will of course try out what you’ve mentioned. But one thing is it has to be new DateTime there because the ("dd:hh:mm:ss") format only works for DateTime, as far as I know. I think I did try that before as well.

Well, reading the documentation usually helps ^^. Also linked from that page are the TimeSpan custom format strings. As you can see other characters have to be escaped. So your format string should look like this:

string time = timeDifference.ToString("dd\:hh\:mm\:ss");

Thanks for that, I couldn’t find the TimeSpan format in the docs, anyways I get the error for Unrecognised sequence, I think it’s because of the \ in there which probably messes with some other stuff?

Yes, my bad. I always forget that most C# MSDN pages now use “@” strings for some reason. So it should be

string time = timeDifference.ToString(@"dd\:hh\:mm\:ss");
// or
string time = timeDifference.ToString("dd\\:hh\\:mm\\:ss");

Just figured that out after reading some docs, thanks though!!! Let me give this a try and see what happens

Yes, All is working fine, even better than my previous script, can I adjust it saying like if the Time difference is like 2 days, 10 hours, format it in dd:hh only, then if the days is 0, format it in hh:mm?

Not with a single format string. However you can of course use two format strings and choose the appropriate one. For example

public static string FormatTime(TimeSpan aTime)
{
    if (aTime.TotalDays >= 1)
        return aTime.ToString(@"dd\:hh");
    return aTime.ToString(@"hh\:mm");
}

You can use this method instead of ToString

Thanks bunny, really helped me out a lot, I’ve ended up with this now

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.UI;
using TMPro;
using System.Globalization;

public class BrawlPassTimer : MonoBehaviour
{
    [SerializeField] DateTime currentDate;
    [SerializeField] DateTime startDate;
    [SerializeField] DateTime endDate;
    public TMP_Text FinishTimeText;
    void Start()
    {
        // Setting timer start and end dates

        currentDate = DateTime.Now; // Checking current date, time etc
        Debug.Log(currentDate); // Checking if it actually shows current date
        startDate = new DateTime(2021, 5, 24, 10, 0, 0); // Set the start date
        Debug.Log(startDate); // Checking if it actually sets the start date                                           
        endDate = new DateTime(2021, 5, 255, 10, 0, 0); // Set the end date
        Debug.Log(endDate); // Checking if it actually sets the end date
        TimeSpan timeDifference = endDate - currentDate; // Seeing what is the time difference
        string time = timeDifference.ToString(@"dd\:hh\:mm\:ss");
        Debug.Log(time);
        FinishTimeText.text = time;

        if(currentDate >= endDate)
        {
            Debug.Log("Event has ended");
            FinishTimeText.text = "Event Ended";
        }
    }

    public static string FormatTime(TimeSpan aTime)
    {
        if (aTime.TotalDays >= 1)
            return aTime.ToString(@"dd\:hh");
        return aTime.ToString(@"hh\:mm");
    }
}

The format isn’t really working, it still displays days as 00

What do you mean? Here’s a .NET Fiddle with two time spans. One that is below a day, so it displays just the hour and the minute like you said. The other is over a day, so it only displays the days and the hours.

That’s exactly what you asked for. Yes, such a behaviour doesn’t really make much sense since you can’t really tell if you’re displaying days and hours or hours and minutes that way. I just implemented what you have asked for and it does exactly that. So when you said it’s not really working, what exactly do you mean by that? What is your expected output and what output do you get?

edit
ps: It seems you did not use our “FormatTime” method at all in your code ^^. So maybe that’s your issue? :slight_smile:

Of course you should replace this line

string time = timeDifference.ToString(@"dd\:hh\:mm\:ss");

with this one

string time = FormatTime(timeDifference);

Declaring methods do not make them magically being used ( . . . with the exception of Unity’s magic methods like Update, Start, … :smile:)

Omg bunny your a legend, thanks a lot all is really helping me, also can you not like change the format, like in the sense of adding Days, and h in front of the numbers, my previous method allowed me to do this but I’ve tried multiple ways but keep getting the incorrect format error?