Store the day of the week and time?
Is there a way for me to store the the record for Tuesday and
Wednesday in one row or do should I have two records?
There are several ways to store multiple time ranges in a single row. @bma already provided a couple of them. That might be useful to save disk space with very simple time patterns. The clean, flexible and "normalized" approach is to store one row per time range.
What is the best way to store the day and time?
Use a timestamp
(or timestamptz
if multiple time zones may be involved). Pick an arbitrary "staging" week and just ignore the date part while using the day and time aspect of the timestamp
. Simplest and fastest in my experience, and all date and time related sanity-checks are built-in automatically. I use a range starting with 1996-01-01 00:00
for several similar applications for two reasons:
- The first 7 days of the week coincide with the day of the month (for
sun = 7
). - It's the most recent leap year (providing Feb. 29 for yearly patterns) at the same time.
Range type
Since you are actually dealing with time ranges (not just "day and time") I suggest to use the built-in range type tsrange
(or tstzrange
). A major advantage: you can use the arsenal of built-in Range Functions and Operators. Requires Postgres 9.2 or later.
For instance, you can have an exclusion constraint building on that (implemented internally by way of a fully functional GiST index that may provide additional benefit), to rule out overlapping time ranges. Consider this related answer for details:
- Preventing adjacent/overlapping entries with EXCLUDE in PostgreSQL
For this particular exclusion constraint (no overlapping ranges per event), you need to include the integer column event_id
in the constraint, so you need to install the additional module btree_gist. Install once per database with:
CREATE EXTENSION btree_gist; -- once per db
Or you can have one simple CHECK
constraint to restrict the allowed time period using the "range is contained by" operator <@
.
Could look like this:
CREATE TABLE event (event_id serial PRIMARY KEY, ...);
CREATE TABLE schedule (
event_id integer NOT NULL REFERENCES event(event_id)
ON DELETE CASCADE ON UPDATE CASCADE
, t_range tsrange
, PRIMARY KEY (event_id, t_range)
, CHECK (t_range <@ '[1996-01-01 00:00, 1996-01-09 00:00)') -- restrict period
, EXCLUDE USING gist (event_id WITH =, t_range WITH &&) -- disallow overlap
);
For a weekly schedule use the first seven days, Mon-Sun, or whatever suits you. Monthly or yearly schedules in a similar fashion.
How to extract day of week, time, etc?
@CDub provided a module to deal with it on the Ruby end. I can't comment on that, but you can do everything in Postgres as well, with impeccable performance.
SELECT ts::time AS t_time -- get the time (practically no cost)
SELECT EXTRACT(DOW FROM ts) AS dow -- get day of week (very cheap)
Or in similar fashion for range types:
SELECT EXTRACT(DOW FROM lower(t_range)) AS dow_from -- day of week lower bound
, EXTRACT(DOW FROM upper(t_range)) AS dow_to -- same for upper
, lower(t_range)::time AS time_from -- start time
, upper(t_range)::time AS time_to -- end time
FROM schedule;
db<>fiddle here
Old sqliddle
ISODOW
instead of DOW
for EXTRACT()
returns 7
instead of 0
for sundays. There is a long list of what you can extract.
This related answer demonstrates how to use range type operator to compute a total duration for time ranges (last chapter):
- Calculate working hours between 2 dates in PostgreSQL
Best Way to store days of the week with Time Spans
I see no reason why a database view couldn't be used here with the help of a current indication field.
Assuming you had a Type 2 Dimension as such:
UserID
,DayOfWeek
,StartWorkTime
,EndWorkTime
,EffectiveStartDate
,EffectiveEndDate
,Current -- Y/N Flags for being current or not current
You could effectively create a database view that pivoted StartWorkTime and EndWorkTime onto UserID and Current columns.
CREATE VIEW [schema].[view_name] AS
SELECT
UserID
,[Current] -- Y = Yes / N = No
,MAX(CASE WHEN DayOfWeek = 2 THEN StartWorkTime ELSE NULL END) AS Mon_StartWorkTime
,MAX(CASE WHEN DayOfWeek = 2 THEN EndWorkTime ELSE NULL END) AS Mon_EndWorkTime
,MAX(CASE WHEN DayOfWeek = 3 THEN StartWorkTime ELSE NULL END) AS Tue_StartWorkTime
,MAX(CASE WHEN DayOfWeek = 3 THEN EndWorkTime ELSE NULL END) AS Tue_EndWorkTime
,MAX(CASE WHEN DayOfWeek = 4 THEN StartWorkTime ELSE NULL END) AS Wed_StartWorkTime
,MAX(CASE WHEN DayOfWeek = 4 THEN EndWorkTime ELSE NULL END) AS Wed_EndWorkTime
,MAX(CASE WHEN DayOfWeek = 5 THEN StartWorkTime ELSE NULL END) AS Thu_StartWorkTime
,MAX(CASE WHEN DayOfWeek = 5 THEN EndWorkTime ELSE NULL END) AS Thu_EndWorkTime
,MAX(CASE WHEN DayOfWeek = 6 THEN StartWorkTime ELSE NULL END) AS Fri_StartWorkTime
,MAX(CASE WHEN DayOfWeek = 6 THEN EndWorkTime ELSE NULL END) AS Fri_EndWorkTime
FROM [database].[schema].[table_name]
WHERE [Current] = 'Y'
GROUP BY
UserID
,[Current]
Then your results would be for one employee working 0800 to 1600 some days and to 1200 other days as such:
UserID Current Mon_StartWorkTime Mon_EndWorkTime Tue_StartWorkTime Tue_EndWorkTime Wed_StartWorkTime Wed_EndWorkTime Thu_StartWorkTime Thu_EndWorkTime Fri_StartWorkTime Fri_EndWorkTime
1 Y 08:00:00.0000000 16:00:00.0000000 08:00:00.0000000 16:00:00.0000000 08:00:00.0000000 16:00:00.0000000 08:00:00.0000000 12:00:00.0000000 08:00:00.0000000 12:00:00.0000000
Storing day of week with time in database
There is no need to store this value in the database. Remember that you can always retrieve the day of week in SQL, like:
using the
DAYNAME
command:SELECT DAYNAME(day) FROM tbl_days
using the
DAYFORMAT
command:SELECT DATE_FORMAT(day, '%W');
More information at the MySQL Reference Manual for the Date and Time Functions
[EDIT]: You can declare the day
column type as TIMESTAMP
, then your INSERT
query could be something like
INSERT INTO tbl_days (day) VALUES (NOW());
or have an explicit input such as:
SET @date = CONCAT($year,'-',$month,'-',$day,'-',$hour,'-',$minute,'-',$second);
INSERT INTO tbl_days (day) VALUES (STR_TO_DATE(@date,'%Y-%c-%e-%k-%i-%s'));
How to store week days + time in the database
You can use two php inbuilt functions
strtotime() and date()
For the database column kindly use DateTime
* Here is how you can do it *
$date_to_go_in_db = date('Y-m-d H:i:s',strtotime($string_date_from_date_picker));
To show date from db with days and Time such as 26th Monday june 2pm etc use this
date('Y-m-D g:i a', strtotime($dataobject->event_date_time_from_db));
What's the best way to store the days of the week an event takes place on in a relational database?
I would avoid the string option for the sense of purity: it adds an extra layer of encoding/decoding that you do not need. It may also mess you up in the case of internationalization.
Since the number of days in a week is 7, I would keep seven columns, perhaps boolean. This will also facilitate subsequent queries. This will also be useful if the tool is ever used in countries where the workweek starts on different days.
I would avoid the lookup because that would be over-normalization. Unless your set of lookup items is not obvious or could possibly change, it's overkill. In the case of days-of-the-week (unlike US states, for example), I would sleep soundly with the fixed set.
Considering the data domain, I don't think that a bitfield would achieve any significant space savings for you and would just make your code more complex.
Finally, a word of warning about the domain: a lot of schools do weird things with their schedules where they "swap days" to balance out an equal number of weekdays of each type over the semester despite holidays. I am not clear about your system, but perhaps the best approach would be to store a table of the actual dates in which the course is expected to take place. This way, if there are two Tuesdays in a week, the teacher can get paid for showing up twice, and the teacher for the Thursday that was canceled will not pay.
Storing and saving day-of-week/time - value pairs in Java
I took Panky1986's advice and used an EnumMap
containing a TreeMap
to store the hours and a nested TreeMap
inside that to store the minutes which in turn stores the data.
/* This is the enumeration which tracks the days */
public enum DAY {WEEKDAY, SATURDAY, SUNDAY};
/* This is the map that stores the timings */
private EnumMap<DAY, TreeMap<Integer, TreeMap<Integer, String>>> mapDays;
Group object that contains store hours by day of week
You could look to open and close and add a new object. Otherwise change the days property.
var data = [{ dayOfWeek: "Monday", open: "8:00 AM", close: "5:00 PM" }, { dayOfWeek: "Tuesday", open: "8:00 AM", close: "5:00 PM" }, { dayOfWeek: "Wednesday", open: "8:00 AM", close: "6:00 PM" }, { dayOfWeek: "Thursday", open: "8:00 AM", close: "5:00 PM" }, { dayOfWeek: "Friday", open: "8:00 AM", close: "5:00 PM" }, { dayOfWeek: "Saturday", open: "8:00 AM", close: "4:00 PM" }], result = data.reduce((r, { dayOfWeek, open, close }) => { if (!r.length || r[r.length - 1].open !== open || r[r.length - 1].close !== close) r.push({ days: dayOfWeek, open, close }); else r[r.length - 1].days = r[r.length - 1].days.split('-').slice(0, 1).concat(dayOfWeek).join('-'); return r; }, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
How to get the day of the week from a date and insert the time into another dataset in R
The very first thing you'll have to do is, change the data type of dt_start
and dt_end
to POXIXct
.
Then you need to create different columns for storing date and time.
Here's how you'll do it:
wrong$dt_start <- as.POSIXct(wrong$dt_start, format = "%d.%m.%Y %H:%M", tz = Sys.timezone())
wrong$opening_time <- format(as.POSIXct(wrong$dt_start), format = "%H:%M")
wrong$dt_start <- as.Date(wrong$dt_start)
wrong <- wrong[, c(1, 2, 5, 3, 4)]
wrong$dt_end <- as.POSIXct(wrong$dt_end, format = "%d.%m.%Y %H:%M", tz = Sys.timezone())
wrong$closing_time <- format(as.POSIXct(wrong$dt_end), format = "%H:%M")
wrong$dt_end <- as.Date(wrong$dt_end)
wrong <- wrong[, c(1, 2, 3, 4, 6, 5)]
wrong$weekday <- weekdays(wrong$dt_start)
I've changed the weekday_code
with weekday
in order to join the dfs
.
new <- c("Friday", "Monday", "Saturday", "Sunday", "Thursday", "Tuesday", "Wednesday")
store$weekday <- new[match(store$weekday, store$weekday_code, nomatch = 0)]
store$weekday_code <- NULL
store <- store[, c(4, 1, 2, 3)]
Then you can left_join
the data frames to get the desired result.
library(dplyr)
wrong2 <- wrong %>%
left_join(store, by = c("weekday")) %>%
mutate(opening_time = coalesce(opening_time.y, opening_time.x)) %>%
mutate(closing_time = coalesce(closing_time.y, closing_time.x)) %>%
select(-opening_time.x, -closing_time.x, -closing_time.y, -opening_time.y)
The output will look like this:
kod_dolg dt_start dt_end person_number weekday workday_sign
1 50000690 2022-02-14 2022-02-14 976291 Monday 1
2 801 2022-02-14 2022-02-14 754105 Monday 1
3 50000690 2022-02-14 2022-02-14 867801 Monday 1
4 801 2022-02-15 2022-02-15 NA Tuesday 1
5 50000690 2022-02-15 2022-02-15 867801 Tuesday 1
6 801 2022-02-15 2022-02-15 NA Tuesday 1
7 801 2022-02-15 2022-02-15 NA Tuesday 1
8 50000690 2022-02-15 2022-02-15 867801 Tuesday 1
9 801 2022-02-16 2022-02-16 NA Wednesday 1
10 50000690 2022-02-16 2022-02-16 976291 Wednesday 1
11 50000690 2022-02-16 2022-02-16 938541 Wednesday 1
12 801 2022-02-17 2022-02-17 NA Thursday 1
13 50000690 2022-02-17 2022-02-17 NA Thursday 1
14 50000690 2022-02-17 2022-02-17 NA Thursday 1
15 50000690 2022-02-17 2022-02-17 NA Thursday 1
16 801 2022-02-18 2022-02-18 NA Friday 1
17 50000690 2022-02-18 2022-02-18 NA Friday 1
18 801 2022-02-18 2022-02-18 NA Friday 1
19 50000690 2022-02-18 2022-02-18 NA Friday 1
20 801 2022-02-18 2022-02-18 NA Friday 1
21 50000690 2022-02-18 2022-02-18 NA Friday 1
22 801 2022-02-12 2022-02-12 NA Saturday 1
23 50000690 2022-02-12 2022-02-12 NA Saturday 1
24 50000690 2022-02-12 2022-02-12 NA Saturday 1
25 801 2022-02-12 2022-02-12 NA Saturday 1
26 801 2022-02-19 2022-02-19 NA Saturday 1
27 50000690 2022-02-19 2022-02-19 NA Saturday 1
28 50000690 2022-02-19 2022-02-19 NA Saturday 1
29 50000690 2022-02-19 2022-02-19 NA Saturday 1
30 801 2022-02-20 2022-02-20 NA Sunday 1
31 50000690 2022-02-20 2022-02-20 NA Sunday 1
32 50000690 2022-02-20 2022-02-20 NA Sunday 1
33 50000690 2022-02-20 2022-02-20 NA Sunday 1
34 801 2022-02-13 2022-02-13 NA Sunday 1
35 50000690 2022-02-13 2022-02-13 NA Sunday 1
36 801 2022-02-13 2022-02-13 NA Sunday 1
37 50000690 2022-02-13 2022-02-13 NA Sunday 1
opening_time closing_time
1 9:00:00 18:00:00
2 9:00:00 18:00:00
3 9:00:00 18:00:00
4 9:00:00 18:00:00
5 9:00:00 18:00:00
6 9:00:00 18:00:00
7 9:00:00 18:00:00
8 9:00:00 18:00:00
9 9:00:00 18:00:00
10 9:00:00 18:00:00
11 9:00:00 18:00:00
12 9:00:00 18:00:00
13 9:00:00 18:00:00
14 9:00:00 18:00:00
15 9:00:00 18:00:00
16 9:00:00 18:00:00
17 9:00:00 18:00:00
18 9:00:00 18:00:00
19 9:00:00 18:00:00
20 9:00:00 18:00:00
21 9:00:00 18:00:00
22 9:00:00 17:00:00
23 9:00:00 17:00:00
24 9:00:00 17:00:00
25 9:00:00 17:00:00
26 9:00:00 17:00:00
27 9:00:00 17:00:00
28 9:00:00 17:00:00
29 9:00:00 17:00:00
30 10:00:00 16:00:00
31 10:00:00 16:00:00
32 10:00:00 16:00:00
33 10:00:00 16:00:00
34 10:00:00 16:00:00
35 10:00:00 16:00:00
36 10:00:00 16:00:00
37 10:00:00 16:00:00
Related Topics
Ruby Read CSV File as Utf-8 And/Or Convert Ascii-8Bit Encoding to Utf-8
How to Count the Number of Records That Have a Unique Value in a Particular Field in Ror
Creating a Model That Has a Tree Structure
How to Set Http_Referer When Testing in Rails
Return Index of All Occurrences of a Character in a String in Ruby
Shell Out from Ruby While Setting an Environment Variable
Routing Nested Resources in Rails 3
Ruby on Rails: Can You Put Ruby Code in a Yaml Config File
How to Url Encode a String in Ruby
Differencebetween Using .Exists, and .Present? in Ruby
How to Install Jekyll on Osx 10.11
How to Automate Chrome Request Blocking Using Selenium-Webdriver for Ruby
Errno::Econnrefused: Connection Refused - Connect(2) for Action Mailer
Is There a Natural_Sort_By Method for Ruby
Heroku and Rails: Gem Load Error with Postgres, However It Is Specified in Gemfile