Domanda

Stiamo costruendo un'app che memorizza "ore di funzionamento" per varie aziende. Qual è il modo più semplice per rappresentare questi dati in modo da poter verificare facilmente se un elemento è aperto?

Alcune opzioni:

  • Segmenta i blocchi (ogni 15 minuti) che puoi contrassegnare come "aperto / chiuso". Il controllo comporta la verifica se l'opzione " open " bit è impostato per l'orario desiderato (un po 'come un orario dei treni).
  • Memorizzare un elenco di intervalli di tempo (11: 00-14: 00, 17: 19, ecc.) e verificare se l'ora corrente rientra in un intervallo specificato (questo è ciò che fa il nostro cervello quando analizza le stringhe sopra).

Qualcuno ha esperienza nella memorizzazione e nella ricerca di informazioni sugli orari e qualche consiglio da dare?

(Ci sono tutti i tipi di casi d'angolo pazzi come " chiuso il primo martedì del mese " ;, ma lo lasceremo per un altro giorno).

È stato utile?

Soluzione

memorizza ogni blocco di tempo contiguo come ora di inizio e durata; questo rende più facile controllare quando le ore superano i limiti di data

se sei certo che le ore di funzionamento non supereranno mai i limiti di data (ovvero non ci sarà mai una vendita aperta tutta la notte o un evento di maratona di 72 ore e così via), gli orari di inizio / fine saranno sufficienti

Altri suggerimenti

La soluzione più flessibile potrebbe essere l'uso dell'approccio bitset. Ci sono 168 ore in una settimana, quindi ci sono 672 periodi di 15 minuti. Questo è solo 84 byte di spazio, che dovrebbe essere tollerabile.

Userei una tabella come questa:

BusinessID | weekDay | OpenTime | CloseTime 
---------------------------------------------
     1          1        9           13
     1          2        5           18
     1          3        5           18
     1          4        5           18
     1          5        5           18
     1          6        5           18
     1          7        5           18

Qui, abbiamo un'attività che ha orari regolari dalle 5 alle 6, ma domenica più brevi.

Una query per if open sarebbe (psuedo-sql)

SELECT @isOpen = CAST
   (SELECT 1 FROM tblHours 
       WHERE BusinessId = @id AND weekDay = @Day 
       AND CONVERT(Currentime to 24 hour) IS BETWEEN(OpenTime,CloseTime)) AS BIT;

Se hai bisogno di archiviare casi limite, allora hai solo 365 voci, una al giorno ... non è poi così tanto nel grande schema delle cose, posiziona un indice sulla colonna del giorno e sulla colonna businessId.

Non dimenticare di memorizzare il fuso orario delle imprese in una tabella separata (normalizza!) ed eseguire una trasformazione tra il tuo tempo e quello prima di fare questi confronti.

Penso che andrei personalmente per un inizio + fine, in quanto renderebbe tutto più flessibile. Una buona domanda sarebbe: qual è la probabilità che la dimensione del blocco cambi ad un certo punto? Quindi scegli la soluzione che si adatta meglio alla tua situazione (se è suscettibile di modifiche, sceglierei sicuramente i periodi).

È possibile memorizzarli come intervallo di tempo e utilizzare i segmenti nell'applicazione. In questo modo hai l'input semplice usando i blocchi, mantenendo la flessibilità di cambiare nel tuo archivio dati.

Per aggiungere a ciò che Johnathan Holland ha detto , consentirei più voci per lo stesso giorno.

Vorrei anche consentire il tempo decimale o un'altra colonna per i minuti.

Perché? molti ristoranti e alcune aziende e molte aziende in tutto il mondo pranzano o fanno pause pomeridiane. Inoltre, molti ristoranti (2 che conosco vicino a casa mia chiudono a intervalli dispari di non 15 incrementi. Uno chiude alle 21:40 la domenica e uno chiude all'01: 40.

Esiste anche il problema delle ore di ferie, come ad esempio la chiusura anticipata dei negozi il giorno del Ringraziamento, quindi è necessario avere una sostituzione basata sul calendario.

Forse ciò che può essere fatto è una data / ora aperta, una data-ora chiusa, come questa:

businessID  | datetime              | type
==========================================
        1     10/1/2008 10:30:00 AM    1
        1     10/1/2008 02:45:00 PM    0
        1     10/1/2008 05:15:00 PM    1
        1     10/2/2008 02:00:00 AM    0
        1     10/2/2008 10:30:00 AM    1

ecc. (digitare: 1 essendo aperto e 0 chiuso)

E avere tutti i giorni nei prossimi 1 o due anni precalcolati con 1-2 anni di anticipo. Nota che avresti solo 3 colonne: int, data / ora / bit, quindi il consumo di dati dovrebbe essere minimo.

Questo ti permetterà anche di modificare date specifiche per ore dispari per giorni speciali, come vengono conosciuti.

Si occupa anche dell'incrocio di mezzanotte e delle conversioni di 12/24 ore.

È anche agnostico nel fuso orario. Se memorizzi l'ora di inizio e la durata, quando calcoli l'ora di fine, la tua macchina ti darà l'ora di regolazione TZ? È questo che vuoi? Più codice.

per quanto riguarda l'interrogazione per lo stato aperto-chiuso: interroga la data-ora in questione,

select top 1 type from thehours where datetimefield<=somedatetime and businessID = somebusinessid order by datetime desc

quindi guarda " digita " ;. se uno è aperto, se 0 è chiuso.

PS: sono stato al dettaglio per 10 anni. Quindi ho familiarità con i problemi delle ore folli delle piccole imprese.

OK, I'll throw in on this for what it's worth.

I need to handle quite a few things.

  • Fast / Performant Query
  • Any increments of time, 9:01 PM, 12:14, etc.
  • International (?) - not sure if this is an issue even with timezones, at least in my case but someone more versed here feel free to chime in
  • Open - Close spanning to the next day (open at noon, close at 2:00 AM)
  • Multiple timespans / day
  • Ability to override specific days (holidays, whatever)
  • Ability for overrides to be recurring
  • Ability to query for any point in time and get businesses open (now, future time, past time)
  • Ability to easily exclude results of businesses closing soon (filter businesses closing in 30 minutes, you don't want to make your users 'that guy that shows up 5 minutes before closing in the food/beverage industry)

I like a lot of the approaches presented and I'm borrowing from a few of them. In my website, project, whatever I need to take into consideration I may have millions of businesses and a few of the approaches here don't seem to scale well to me personally.

Here's what I propose for an algorithm and structure.

We have to make some concrete assumptions, across the globe, anywhere, any time: There are 7 days in a week. There are 1440 minutes in one day. There are a finite number of permutations of minutes of open / closed that are possible.

Not concrete but decent assumptions: Many permutations of open/closed minutes will be shared across businesses reducing total permutations actually stored. There was a time in my life I could easily calculate the actual possible combinations to this approach but if someone could assist/thinks it would be useful, that would be great.

I propose 3 tables: Before you stop reading, consider in the real-world 2 of these tables will be small enough cache neatly. This approach isn't going to be for everyone either due to the sheer complexity of code required to interpret a UI to the data model and back again if needed. Your mileage and needs may vary. This is an attempt at a reasonable 'enterprise' level solution, whatever that means.

HoursOfOperations Table

ID | OPEN (minute of day) | CLOSE (minute of day)


1 | 360 | 1020 (example: 9 AM - 5 PM)

2 | 365 | 1021 (example: edge-case 9:05 AM - 5:01 PM (weirdos) )

etc.

HoursOfOperations doesn't care about what days, just open and close and uniqueness. There can be only a single entry per open/close combination. Now, depending on your environment either this entire table can be cached or it could be cached for the current hour of the day, etc. At any rate, you shouldn't need to query this table for every operation. Depending on your storage solution I envision every column in this table as indexed for performance. As time progresses, this table likely has an exponentially inverse likelihood of INSERT(s). Really though, dealing with this table should mostly be an in-process operation (RAM).

Business2HoursMap

Note: In my example I'm storing "Day" as a bit-flag field/column. This is largely due to my needs and the advancement of LINQ / Flags Enums in C#. There's nothing stopping you from expanding this to 7 bit fields. Both approaches should be relatively similar in both storage logic and query approach.

Another Note: I'm not entering into a semantics argument on "every table needs a PK ID column", please find another forum for that.

BusinessID | HoursID | Day (or, if you prefer split into: BIT Monday, BIT Tuesday, ...)


1 | 1 | 1111111 (this business is open 9-5 every day of the week)

2 | 2 | 1111110 (this business is open 9:05 - 5:01 M-Sat (Monday = day 1)

The reason this is easy to query is that we can always determine quite easily the MOTD (Minute of the Day) that we're after. If I want to know what's open at 5 PM tomorrow I grab all HoursOfOperations IDS WHERE Close >= 1020. Unless I'm looking for a time range, Open becomes insignificant. If you don't want to show businesses closing in the next half-hour, just adjust your incoming time accordingly (search for 5:30 PM (1050), not 5:00 PM (1020). The second query would naturally be 'give me all business with HoursID IN (1, 2, 3, 4, 5), etc. This should probably raise a red flag as there are limitations to this approach. However, if someone can answer the actual permutations question above we may be able to pull the red flag down. Consider we only need the possible permutations on any one side of the equation at one time, either open or close.

Considering we've got our first table cached, that's a quick operation. Second operation is querying this potentially large-row table but we're searching very small (SMALLINT) hopefully indexed columns.

Now, you may be seeing the complexity on the code side of things. I'm targeting mostly bars in my particular project so it's going to be very safe to assume that I will have a considerable number of businesses with hours such as "11:00 AM - 2:00 AM (the next day)". That would indeed be 2 entries into both the HoursOfOperations table as well as the Business2HoursMap table. E.g. a bar that is open from 11:00 AM - 2:00 AM will have 2 references to the HoursOfOperations table 660 - 1440 (11:00 AM - Midnight) and 0 - 120 (Midnight - 2:00 AM). Those references would be reflected into the actual days in the Business2HoursMap table as 2 entries in our simplistic case, 1 entry = all days Hours reference #1, another all days reference #2. Hope that makes sense, it's been a long day.

Overriding on special days / holidays / whatever. Overrides are by nature, date based, not day of week based. I think this is where some of the approaches try to shove the proverbial round peg into a square hole. We need another table.

HoursID | BusinessID | Day | Month | Year

1 | 2 | 1 | 1 | NULL

This can certainly get more complex if you needed something like "on every second Tuesday, this company goes fishing for 4 hours". However, what this will allow us to do quite easily is allow 1 - overrides, 2 - reasonable recurring overrides. E.G. if year IS NULL, then every year on New Years day this weirdo bar is open from 9:00 AM to 5:00 PM keeping in line with our above data examples. I.e. - If year were set, it's only for 2013. If month is null, it's every first day of the month. Again, this won't handle every scheduling scenario by NULL columns alone, but theoretically, you could handle just about anything by relying on a long sequence of absolute dates if needed.

Again, I would cache this table on a rolling day basis. I just can't realistically see the rows for this table in a single-day snapshot being very large, at least for my needs. I would check this table first as it is well, an override and would save a query against the much larger Business2HoursMap table on the storage-side.

Interesting problem. I'm really surprised this is the first time I've really needed to think this through. As always, very keen on different insights, approaches or flaws in my approach.

The segment blocks are better, just make sure you give the user an easy way to set them. Click and drag is good.

Any other system (like ranges) is going to be really annoying when you cross the midnight boundary.

As for how you store them, in C++ bitfields would probably be best. In most other languages, and array might be better (lots of wasted space, but would run faster and be easier to comprehend).

I would think a little about those edge-cases right now, because they are going to inform whether you have a base configuration plus overlay or complete static storage of opening times or whatever.

There are so many exceptions - and on a regular basis (like snow days, irregular holidays like Easter, Good Friday), that if this is expected to be a reliable representation of reality (as opposed to a good guess), you'll need to address it pretty soon in the architecture.

How about something like this:

Store Hours Table

Business_id (int)
Start_Time (time)
End_Time (time)
Condition varchar/string
Open bit

'Condition' is a lambda expression (text for a 'where' clause). Build the query dynamically. So for a particular business you select all of the open/close times

Let Query1 = select count(open) from store_hours where @t between start_time and end_time and open  = true and business_id = @id and (.. dynamically built expression)

Let Query2 = select count(closed) from store_hours where @t between start_time and end_time and open = false and business_id = @id and (.. dynamically built expression)

So end the end you want something like:

select cast(Query1 as bit) & ~cast(Query2 as bit)

If the result of the last query is 1 then the store is open at time t, otherwise it is closed.

Now you just need a friendly interface that can generate your where clauses (lambda expressions) for you.

The only other corner case that I can think of is what happens if a store is open from say 7am to 2am on one date but closes at 11pm on the following date. Your system should be able to handle that as well by smartly splitting up the times between the two days.

There is surely no need to conserve memory here, but perhaps a need for clean and comprehensible code. "Bit twiddling" is not, IMHO, the way to go.

We need a set container here, which holds any number of unique items and can determine quickly and easily whether an item is a member or not. The setup reuires care, but in routine use a single line of simply understood code determines if you are open or closed

Concept: Assign index number to every 15 min block, starting at, say, midnight sunday.

Initialize: Insert into a set the index number of every 15 min block when you are open. ( Assuming you are open fewer hours than you are closed. )

Use: Subtract from interesting time, in minutes, midnight the previous sunday and divide by 15. If this number is present in the set, you are open.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top