"Get a place's open and close hours and if DateTime.Now is between mark as open " works locally but not on the server after published

Viewed 108

I have a problem with times when debugging locally versus when published on a server.

My need is to know if a store is open between 2 times such as:

if ((timeNow >= openHour) && (timeNow <= closeHour))

And if it is, tell the user if the store is closed or open.

Edit

My question basically: The Cron Job that updates the stores' state every minute queries the stores' timezones and for each store adapt its DateTime.UtcNow to that timezone, and it works when debugging locally but not when I've published the code and query the stores' state.

Here's what I'm trying to do:

StoreHours.cs

public class StoreHour : AuditableEntities
{
    public StoreHour()
    {

    }

    public Guid StoreHourId { get; set; }
    public Guid DayOfTheWeekId { get; set; }
    public virtual DayOfTheWeek DayOfTheWeek { get; set; }
    public DateTime OpenHour { get; set; }
    public DateTime CloseHour { get; set; }
    public Guid PlaceId { get; set; }
    public virtual Place Place { get; set; }
}

AppDbContext.cs ( seed data )

// Hours for the first store
var cscOpeningHour = DateTime.ParseExact("11:30:00 AM", "hh:mm:ss tt", CultureInfo.InvariantCulture);
cscOpeningHour = DateTime.SpecifyKind(cscOpeningHour, DateTimeKind.Utc);
var cscClosingHour = DateTime.ParseExact("10:00:00 PM", "hh:mm:ss tt", CultureInfo.InvariantCulture);
cscClosingHour = DateTime.SpecifyKind(cscClosingHour, DateTimeKind.Utc);

// Hours for the second store ( have multiple because closing time is the next day ( 02:00:00 AM )
var jOpeningHour1 = DateTime.ParseExact("12:00:00 AM", "hh:mm:ss tt", CultureInfo.InvariantCulture);
jOpeningHour1 = DateTime.SpecifyKind(jOpeningHour1, DateTimeKind.Utc);
var jClosingHour1 = DateTime.ParseExact("02:00:00 AM", "hh:mm:ss tt", CultureInfo.InvariantCulture);
jClosingHour1 = DateTime.SpecifyKind(jClosingHour1, DateTimeKind.Utc);
var jOpeningHour2 = DateTime.ParseExact("05:00:00 PM", "hh:mm:ss tt", CultureInfo.InvariantCulture);
jOpeningHour2 = DateTime.SpecifyKind(jOpeningHour2, DateTimeKind.Utc);
var jClosingHour2 = DateTime.ParseExact("11:59:59 PM", "hh:mm:ss tt", CultureInfo.InvariantCulture);
jClosingHour2 = DateTime.SpecifyKind(jClosingHour2, DateTimeKind.Utc);

// Seed data
modelBuilder.Entity<StoreHour>().HasData(new StoreHour
        {
            StoreHourId = Guid.NewGuid(),
            DayOfTheWeekId = mondayDayOfTheWeekId,
            OpenHour = cscOpeningHour,
            CloseHour = cscClosingHour,
            PlaceId = cscPlaceId
        });

UpdateDeviceCommandHandler.cs

public async Task<Unit> Handle(UpdateDeviceCommand request, CancellationToken cancellationToken)
    {
        var devices = await repository.GetAllDevicesWithRelatedData();
        foreach (var device in devices)
        {
            var count = await repository.GetNumber(device.DeviceId);

            var thePlace = await placeRepository.GetPlaceByIdWithRelatedData(device.Place.PlaceId);

            var timeNow = TimeZoneInfo.ConvertTime(
                              DateTime.Now,
                              TimeZoneInfo.FindSystemTimeZoneById(thePlace.Timezone.Name));

            var day = DateTime.UtcNow.DayOfWeek.ToString();

            if (thePlace.StoreHours.Count() > 7)
            {
                // OpenHour and CloseHour are DateTime properties
                // returns "12:00:00 AM"
                var openHour1 = thePlace.StoreHours.OrderBy(d => d.OpenHour).Select(d => d.OpenHour).First().ToString("hh:mm:ss tt");
                // returns "2:00:00 AM"
                var closeHour1 = thePlace.StoreHours.OrderBy(d => d.CloseHour).Select(d => d.CloseHour).First().ToString("hh:mm:ss tt");
                // returns "05:00:00 PM"
                var openHour2 = thePlace.StoreHours.OrderByDescending(d => d.OpenHour).Select(d => d.OpenHour).First().ToString("hh:mm:ss tt");
                // returns "11:59:59 PM"
                var closeHour2 = thePlace.StoreHours.OrderByDescending(d => d.CloseHour).Select(d => d.CloseHour).First().ToString("hh:mm:ss tt");

                // returns today's date and time: mm/dd/yyyy - 12:00:00 AM
                // Which is the right time converted to the place's timezone
                var theOpenHour1 = DateTime.Parse(openHour1);
                theOpenHour1 = TimeZoneInfo.ConvertTime(
                              theOpenHour1,
                              TimeZoneInfo.FindSystemTimeZoneById(thePlace.Timezone.Name));
                var theCloseHour1 = DateTime.Parse(closeHour1);
                theCloseHour1 = TimeZoneInfo.ConvertTime(
                              theCloseHour1,
                              TimeZoneInfo.FindSystemTimeZoneById(thePlace.Timezone.Name));
                var theOpenHour2 = DateTime.Parse(openHour2);
                theOpenHour2 = TimeZoneInfo.ConvertTime(
                              theOpenHour2,
                              TimeZoneInfo.FindSystemTimeZoneById(thePlace.Timezone.Name));
                var theCloseHour2 = DateTime.Parse(closeHour2);
                theCloseHour2 = TimeZoneInfo.ConvertTime(
                              theCloseHour2,
                              TimeZoneInfo.FindSystemTimeZoneById(thePlace.Timezone.Name));

                if ((timeNow >= theOpenHour1) && (timeNow <= theCloseHour1)
                     || (timeNow >= theOpenHour2) && (timeNow <= theCloseHour2))
                {
                    if (count != 0)
                    {
                        device.Ratio = (double)device.Count / (double)count;
                    }
                    else
                    {
                        count += 1;
                        device.Ratio = (double)device.Count / (double)count;
                    }

                    if (device.Ratio == 2)
                    {
                        //
                    }
                    if (...)
                    {
                        //
                    }

                    repository.Update(device);
                }
                else
                {
                    //

                    repository.Update(device);
                }
            }

            else
            {
                var openHour = thePlace.StoreHours.Select(d => d.OpenHour).First().ToString("hh:mm:ss tt");
                var closeHour = thePlace.StoreHours.Select(d => d.CloseHour).First().ToString("hh:mm:ss tt");

                DateTime dtOpen = DateTime.Parse(openHour);
                dtOpen = TimeZoneInfo.ConvertTime(
                              dtOpen,
                              TimeZoneInfo.FindSystemTimeZoneById(thePlace.Timezone.Name));
                DateTime dtClose = DateTime.Parse(closeHour);
                dtClose = TimeZoneInfo.ConvertTime(
                              dtClose,
                              TimeZoneInfo.FindSystemTimeZoneById(thePlace.Timezone.Name));

                if ((timeNow >= dtOpen) && (timeNow <= dtClose))
                {
                    if (count != 0)
                    {
                        device.Ratio = (double)device.Count/ (double)count;
                    }
                    else
                    {
                        count += 1;
                        device.Ratio = (double)device.Count/ (double)count;
                    }

                    if (device.Ratio == 2)
                    {
                        //
                    }
                    if (...)
                    {
                        //
                    }

                    repository.Update(device);
                }
                else
                {
                    //

                    repository.Update(device);
                }
            }

        await repository.SaveChanges();

        return Unit.Value;

        }

As @JonasH said I should use TimeSpan but before I do I really want to understand why it's working locally and not remotely. I'm converting the invalid date's time "12:00:00 AM" into a valid DateTime converted to the place's TimeZone ( right before the if statement ). When I debug it it works because the times compared are the same day and the right times, what confuses me is why not on the server, what is it that I'm not converting or not doing that makes the server not do it.

I'm sure it has to do with time conversion as my computer gets the local time but I don't know how the server gets it wrong after having converted the time values in the code.

Thank you so much for your help !

2 Answers

My need is to know if a store is open between 2 times.

I would suggest storing the opening/closing times as TimeSpans representing the time of day (i.e. the time from the start of the day), and the timezone for the store.

.Net 6 are introducing a TimeOnly type that provides a better representation of a "time of day" than a timespan. And there is also NodaTime for a more complete set of date/time related types.

You can then use TimeZoneInfo to get the current time of day for a specific time-zone:

var timeOfDayForStore = TimeZoneInfo.ConvertTime(
                                  DateTime.Now,
                                  TimeZoneInfo.FindSystemTimeZoneById(myStoreTimeZone))
                                 .TimeOfDay;
if(myStoreOpenTime < timeOfDayForStore && myStoreCloseTime > timeOfDayForStore){
    ...
}

Note that this assumes the store is not open over midnight.

If you get a DateTime from a javascript / typescript client it should contain information about the timezone and the comparison should work. One common pitfall is if you store this value in a database using DateTime (and not DateTimeOffset) you will lose this information and the comparison (after round trip to DB) will be wrong.

It is a bit unclear what you're asking for but it might be that you should just store the openHour and 'closeHour' as UTC (or with offset information). It looks like you're always getting timeNow on the server so maybe it's just that?

Related