0

I created some code and classes to get a random Hour and Minute between current time (NOW) and the End of the Day. My code works, but some times the times are wrong.

Lets say it's 11:30 PM and I run the code, I get WRONG output, where the write output is shown below... The values Must be in order from NOW (when executed) to End of the Current Day.

Does anyone know what I'm doing wrong?

WRONG (hour/minute):

  • 0:23 - 12:23 AM (next day)
  • 23:15 - 11:15 PM

RIGHT (hour/minute):

  • 23:15 - 11:15 PM
  • 0:23 - 12:23 AM (next day)
public class MyHourMinuteTestClass
{
   ArrayList projects  = new ArrayList();
   TimeSpan start = DateTime.Now.TimeOfDay;
   TimeSpan s = TimeSpan.FromHours(DateTime.Now.Hour);
   TimeSpan e = TimeSpan.FromHours(23.99);
   int maxMin = (int)((e - s).TotalMinutes);

   public MyHourMinuteTestClass()
   {
   }

   public void TestValues()
   {
       // create 2 sets of values and add a new SocialTimes instance containing the hour/minute to the array list
      for(int i = 0; i < 2; i++)
      {
          int minutes = randomGen.Next(maxMin);
          TimeSpan t = start.Add(TimeSpan.FromMinutes(minutes));
          int PostTimeHour = t.Hours;
          int PostTimeMinutes = t.Minutes;
          projects.Add(new SocialTimes(NowDateStr, PostTimeHour, PostTimeMinutes));
      }

      // then I sort the projects array list
      projects.Sort(new hoursMinutesComparer());
    }
}

public class SocialTimes
{
        string DateToPostStr = "";
        public int HourToPost = 0;
        public int MinuteToPost = 0;

        public SocialTimes(string DateToPostStr, int HourToPost, int MinuteToPost)
        {
            this.DateToPostStr = DateToPostStr;
            this.HourToPost = HourToPost;
            this.MinuteToPost = MinuteToPost;
        }

        public string getDateToPostStr()
        {
            return this.DateToPostStr;
        }

        public int getHourToPost()
       {
            return this.HourToPost;
        }

        public int getMinuteToPost()
        {
            return this.MinuteToPost;
        }

        public int getHourMinuteToPost()
        {
            int sum;

            sum = HourToPost * 60 + MinuteToPost;

            return sum;
        }
 }

public class hoursMinutesComparer : IComparer
{
     int IComparer.Compare(Object xx, Object yy)
     {
         SocialTimes x = (SocialTimes)xx;
         SocialTimes y = (SocialTimes)yy;

         return x.getHourMinuteToPost().CompareTo(y.getHourMinuteToPost());
      }
 }
GRF
  • 171
  • 2
  • 10
  • What I would probably do is figure out how many minutes, since that seems to be your precision, are left in the day. Choose a random value in that range, add those minutes to the time now, then format it as you wish. – Retired Ninja Apr 03 '20 at 05:07
  • 1
    Does this answer your question? [Random date in C#](https://stackoverflow.com/questions/194863/random-date-in-c-sharp) – jason.kaisersmith Apr 03 '20 at 05:16
  • How can `23:15 - 11:15 PM` be both right and wrong? – Enigmativity Apr 03 '20 at 06:48
  • Try this: `var r = new Random(); var n = DateTime.Now; var result = n.AddSeconds(r.NextDouble() * n.AddDays(1.0).Date.Subtract(n).TotalSeconds);`. – Enigmativity Apr 03 '20 at 06:51

2 Answers2

0

That's some wordy code. Let's see if I can reduce it a bit/give a code block you can use for inspiration (because this seems like a homework)

public class Project{
  public Guid ID { get; set;}
  public DateTime Created{ get; set; }
}

public static class ProjectFactory{
  private static Random _r = new Random();

  public static Project CreateProject(){

    DateTime midnightTomorrow = DateTime.Today.AddDays(1);
    int secTilDayEnd = (int)(midnightTomorrow - DateTime.Now).TotalSeconds; 

    return new Project(){
      ID = Guid.NewGuid(),
      Created = DateTime.Now.AddSeconds(_r.Next(secTilDayEnd))
    };
  }
}

//and to use it

List<Project> p = new List<Project>(1000);
for(int i = 0; i < 1000; i++)
  p.Add(ProjectFactory.CreateProject());

foreach(var x in p.OrderBy(proj => proj.Created))
  Console.WriteLine($"Project {x.Id} created {x.Created:HH:mm}");

Let's store the random time in a datetime; we can always dig the time bit out if we want only that but keeping the date is helpful to retain. It also means we can just use default date time comparisons built into c# - it knows how to order dates

I created a class that has a sole job to manufacture projects with a created date of some time between now and the end of the day. It does this by working out how many seconds til the end of the day and adding a random number of those seconds to the current time

And then a code fragment to create a thousand projects, order them by the date and print them out, showing the time only

Caius Jard
  • 72,509
  • 5
  • 49
  • 80
  • Why did you add a `Guid` ID? – Enigmativity Apr 03 '20 at 07:03
  • To a) show how to add another property, b) to show how to init multiple properties c) to tell them apart in a console printout - if run in the final 45 minutes of a day it's certain that a list of 1000 projects would have duplicates on HH:mm only d) because its easy to init to a unique value .. – Caius Jard Apr 03 '20 at 07:32
  • Thanks Caius... These classes are very clean and makes sense. But I'm confused as to why there is a variable called midnightTomorrow being used. It should only create projects between NOW and the end of day. Can you explain? Also, for others, the return code has syntax errors. `return new Project() { ID = Guid.NewGuid(), Created = DateTime.Now.AddSeconds(_r.Next(secTilDayEnd)) };` – GRF Apr 03 '20 at 15:06
  • Also, how can I sort the list p after all the projects are added to the list? Do I just call this after the for loop? `p.OrderBy(proj => proj.Created);` – GRF Apr 03 '20 at 16:01
  • midnight is the first thing that happens in a day, the 0 time that counts up to 23:59 then back to zero. If it is currently 23:30 on Monday, midnightTomorrow is midnight on Tuesday, in 30 minutes time. I call it tomorrow because today is Monday and tomorrow is Tuesday. You can call the variable midnightTonight if it makes more sense to you, but consider than if it were 1am "midnight tonight" could reasonably be the midnight that happened an hour ago, because you're still in "this night" (the origin of the word "tonight") i.e. the current period of darkness. Avoidable ambiguities – Caius Jard Apr 03 '20 at 16:10
  • `p.OrderBy(proj => proj.Created)` temporarily sorts the list because OrderBy returns a sorted version of the list but it doesn't change the ordering of the original list. If you called it on its own, the sorted list would be immediately thrown away and not used. This in contrast to something like `Array.Sort(..)` which takes an array and permanently sorts it (changes the order of the array). The LINQ OrderBy is probably a far more common construct these days because it's easy to specify what you want to sort by and it works on anything that can be enumerated. For example ... – Caius Jard Apr 03 '20 at 16:20
  • `people.OrderBy(person => person.Name)` -people is an enumerable thing (eg a Person[] array, List etc), the operation is orderby and the thing you put in the brackets is a lambda expression; a sort of tiny method that has a variable name on the left of the `=>` and an expression that is a simple value on the right. As LINQ travels through the list of people, each person it encounters is temporarily stashed in the variable and your code gets its one shot at returning something sortable (like an int, date etc) on the right of the `=>`.variable on the left,sortable expression on the right – Caius Jard Apr 03 '20 at 16:28
  • It can have any in depth logic, and it can be any weird thing so long as it follows these rules. For example `people.OrderBy(person => person.EducationLevel == "PostUniversity" ? person.GraduationDate : person.Birthdate)` for example, will cause people to be sorted by their grad date if they have a university degree, otherwise sort by their birthday. Each person will have their education level tested and either their grad date or birth date returned, then all the dates are used by LINQ to do the sort – Caius Jard Apr 03 '20 at 16:31
  • Fixed the semicolon issue on that line of code you mentioned, thanks! – Caius Jard Apr 03 '20 at 16:43
  • Thanks... How can I permanently sort the list after all projects are added to the List? As you said, I tried calling `p.OrderBy(proj => proj.Created);` but it doesn't work. – GRF Apr 03 '20 at 16:51
0

I think part of your problem is this...

TimeSpan s = TimeSpan.FromHours(DateTime.Now.Hour);

That is basically rounding down the current time to the nearest hour. If it's currently 11:30 PM, then...

// This is pseudocode
s = 23h
e = 24h
maxMin = e - s = 1h

So, you are allowing randomGen.Next(maxMin) to generate a number of minutes up to a full hour even though it's less than an hour to midnight. To fix this, I think you just need to get the total time represented in hours...

TimeSpan s = TimeSpan.FromHours(DateTime.Now.TimeOfDay.TotalHours);

Then you'd have...

// This is pseudocode
s = 23h30m
e = 24h
maxMin = e - s = 30m

Therefore, randomGen.Next(maxMin) could result in at most, (just under) 30 minutes, taking you to midnight.

A few other notes:

  • I think using 23.99 makes it impossible to ever calculate 11:59 PM.
    TimeSpan s = TimeSpan.FromHours(DateTime.Now.TimeOfDay.TotalHours); //  23h30m
    TimeSpan e = TimeSpan.FromHours(23.99);                             // <24h
    //                 (e - s).TotalMinutes                             //    <30m
    int maxMin = (int)((e - s).TotalMinutes);                           //     29m
    int minutes = randomGen.Next(maxMin);                               //     28m (Max)
    TimeSpan t = start.Add(TimeSpan.FromMinutes(minutes));              //  23h58m
    
    e - s is just under 30 minutes to midnight. (int) truncates that to an even 29 minutes. The parameter to Random.Next() is the exclusive upper bound, so passing 29 means the highest value it can return is only 28.
  • It seems like it would simplify things for SocialTimes to just store its data as a TimeSpan or even DateTime and then expose Hour and Minute properties, if desired.
  • Be sure to look into the List<> generic class, which long ago superseded ArrayList.
Lance U. Matthews
  • 15,725
  • 6
  • 48
  • 68
  • Thanks BACON. I changed that line of code you suggested. Makes sense. Now I just have to wait till the end of the day to try it. – GRF Apr 03 '20 at 15:03
  • Well, nothing stopping you changing the clock on your computer – Caius Jard Apr 03 '20 at 16:35
  • Caius... How can I sort the list p after all the projects are added to the list using your classes? – GRF Apr 03 '20 at 23:43