I am having a bit of tough time wrapping my head around this. I have a column based on response time in hours and our company's SLA (service level agreement) is that all incoming inquires should be answered within 2 days (the response time in hours is total hours spent on responding to inquiry).
The problem is that our company operates with winter time (7 h 45 min) and summer time (7 h). My dataset consist both and I want Power BI to differientiate winter and summer time when I try to compute SLA. In winter time 1 working day = 7 h 45 min and in summer time = 7 h. I have just used the average of summer/winter time = 7 h 30 min. The SLA column consist 3 data types, "Innen en arbeidsdag", "Innen to arbeidsdager" and "over 2 arbeidsdager".
My dataset is called 'Masterdata' and I have used this syntax so far:
SLA = SWITCH(TRUE(),Masterdata[Svartid i t]>15,
"Over to arbeidsdager", Masterdata[Svartid i t]>7.5,
"Innen to arbeidsdager", Masterdata[Svartid i t]<=7.5, "Innen en arbeidsdag")
my columns 'Svartid i t' (response time) 'SLA' is so far ok and looks like this:
Svartid i t SLA
6,12 Innen en arbeidsdag
11,73 Innen to arbeidsdager
20,42 Over to arbeidsdager
1,07 Innen en arbeidsdag
etc etc
But how can I use DATES in this syntax to tell Power BI that 'Svartid i t' (response time) YTD column from 15th May to 15th September is summer time, Working day = 7 h?
Summer time: 'Svartid i t' <= 7, "Innen en arbeidsdag", 'Svartid i t' > 7, "Innen to arbeidsdager", 'Svartid i t' > 14, "Over to arbeidsdager"
Winter time: 'Svartid i t' <= 7.75, "Innen en arbeidsdag", 'Svartid i t' > 7.75, "Innen to arbeidsdager", 'Svartid i t' > 15.5, "Over to arbeidsdager"
You can use this formula:
VAR hours =
IF( (MONTH(MasterData[YTD]) >= 5 && DAY(MasterData[YTD]) >= 15)
&& (MONTH(MasterData[YTD]) <= 9 && DAY(MasterData[YTD]) <= 15),
7, 7.45 )
RETURN
SWITCH (
TRUE (),
Masterdata[Svartid i t] > 2 * hours, "Over to arbeidsdager",
Masterdata[Svartid i t] > hours, "Innen to arbeidsdager",
Masterdata[Svartid i t] <= hours, "Innen en arbeidsdag"
)
Make sure to put it in a calculated column. Your sample data is not suitable to verify the formula, so I changed it. But the result will look like this:
Related
I started playing with Tableau. My data looks like below.
I want to create Calculated Field that divides my viz by color. I've got problem with dealing with dates. As you can see variable Quarter consists quarters of each year. What I want is to get rid of those gaps. I know my calculated field is done wrong but I tried so many different options and none of them worked. Basically I want each President starts (for example Donald Trump) his president's seat at 01.01.2017 and finishes it at 31.12.2020.
How should I write it to actually work?
IF YEAR([Quarter]) >= 1981 and YEAR([Quarter]) <= 1989 then 'Ronald Reagan'
ELSEIF YEAR([Quarter]) >= 1989 and YEAR([Quarter]) <= 1993 then 'George H. W. Bush'
ELSEIF YEAR([Quarter]) >= 1993 and YEAR([Quarter]) <= 2001 then 'Bill Clinton'
ELSEIF YEAR([Quarter]) >= 2001 and YEAR([Quarter]) <= 2009 then 'George W. Bush'
ELSEIF YEAR([Quarter]) >= 2009 and YEAR([Quarter]) <= 2017 then 'Barack Obama'
ELSEIF YEAR([Quarter]) >= 2017 and YEAR([Quarter]) <= 2021 then 'Donald Trump'
END
The breaks you see in the line aren’t caused by your calculation, but here are two suggestions nonetheless - and some other tips.
Compare against complete dates, either using date literals (enclosed in # signs) or calling a function like MakeDate(). And you can simplify and speed up your calc by taking advantage of the knowledge that prior tests failed - I.e. to not repeat those tests, as shown below.
if [Quarter] < #01/20/1981# then ‘Historical’
elseif [Quarter] < #01/20/1989# then 'Ronald Reagan'
elseif [Quarter] < #01/20/1993# then 'George H. W. Bush'
elseif [Quarter] < #01/20/2001# then 'Bill Clinton'
elseif [Quarter] < #01/20/2009# then 'George W. Bush''
elseif [Quarter] < #01/20/2017# then 'Barack Obama'
elseif [Quarter] < #01/20/2021# then 'Donald Trump'
else ‘Joe Biden’
end
#AniGoyal’s suggestion to make a separate table and use a join is a good idea too, but the join calc won’t look like the one above - hint - it would have 2 tests
As to the gaps between marks ...
After you place this field on the color shelf, try changing its role from dimension to attribute (and back) to see which view you prefer. You could also directly drag the tiles in your color legend to sort them in chronological order.
Would be nice to have this in the standard Elixir library, but we don't.
Date.add(date, n, :month) # where n could be +/-
How would you implement this?
This looks like a good starting point: https://stackoverflow.com/a/53407676/44080
Date.utc_today() |> Timex.shift(months: -1)
You could use the Timex implementation:
defp shift_by(%NaiveDateTime{:year => year, :month => month} = datetime, value, :months) do
m = month + value
shifted =
cond do
m > 0 ->
years = div(m - 1, 12)
month = rem(m - 1, 12) + 1
%{datetime | :year => year + years, :month => month}
m <= 0 ->
years = div(m, 12) - 1
month = 12 + rem(m, 12)
%{datetime | :year => year + years, :month => month}
end
# If the shift fails, it's because it's a high day number, and the month
# shifted to does not have that many days. This will be handled by always
# shifting to the last day of the month shifted to.
case :calendar.valid_date({shifted.year,shifted.month,shifted.day}) do
false ->
last_day = :calendar.last_day_of_the_month(shifted.year, shifted.month)
cond do
shifted.day <= last_day ->
shifted
:else ->
%{shifted | :day => last_day}
end
true ->
shifted
end
end
Timex uses the MIT license, so you should be able to incorporate this in pretty much any project.
ex_cldr_calendars can also do basic date math for adding and subtracting years, quarters, months, weeks and days for any calendar that implements the Calendar behaviour.
iex> Cldr.Calendar.plus ~D[2019-03-31], :months, -1
~D[2019-02-28]
# The :coerce option determines whether to force an end
# of month date when the result of the operation is an invalid date
iex> Cldr.Calendar.plus ~D[2019-03-31], :months, -1, coerce: false
{:error, :invalid_date}
Without adding a dependency like Timex, the following works for adding/subtracting Gregorian months without too much trouble - assuming you only need the first of each month. Shifting to a day of the month directly may be best served through a library, given how many calendrical fallacies there are.
defmodule DateUtils
#doc """
Shift a given date forward or back n months
"""
def shift_n_months(date, n) when n < 0, do: subtract_n_months(date, -1 * n)
def shift_n_months(date, n), do: add_n_months(date, n)
def add_n_months(date, 0), do: Date.beginning_of_month(date)
def add_n_months(date, n) do
date
|> Date.end_of_month()
|> Date.add(1)
|> add_n_months(n - 1)
end
def subtract_n_months(date, 0), do: Date.beginning_of_month(date)
def subtract_n_months(date, n) do
date
|> Date.beginning_of_month()
|> Date.add(-1)
|> subtract_n_months(n - 1)
end
end
There is an elixir function Date.add/2. Give it any date and it will add the dates for you.
iex>Date.add(~D[2000-01-03], -2)
~D[2000-01-01]
If you want to create the date to add to then i suggest you use the Date.new/4
iex>{:ok, date} = Date.new(year, month, day)
iex>date |> Date.add(n)
I need to calculate the date between two dates, and tell me how many day are in between, if more than 30 days then I will target something.
in this script D is the date (in the past) that I want to calculate from today
set X to MYdatefromSafari -- "August 26th, 2016"
set D to ConvertDate(X)
log D
on ConvertDate(X) -- sub routine to convert string "english_month dayth/st, year" to real date
set MS to {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
set LW to every word of X
if (count of LW) is not 3 then return "" -- invalid format
set MI to 0 -- check month : should be in the list
repeat with I from 1 to 12
if item I of MS is item 1 of LW then set MI to I
end repeat
if MI is 0 then return "" -- the fisrt word is not in the list of months
try -- check day : it should be NNth of NNst
set DI to (text 1 thru -3 of item 2 of LW) as integer
end try
if not ((DI > 0) and (DI < 31)) then return "" -- invalid day
try -- check year
set YI to (item 3 of LW) as integer
end try
if not ((YI > 0) and (YI < 9999)) then return "" -- invalid year
return date ((DI & "/" & MI & "/" & YI) as string)
end ConvertDate
In the best scenario, that would calculate the number of date in between if less than a year, and month or year if more
EDIT :
set X to "August 26th, 2016"
set MyDate to ConvertDate(X)
set D to ConvertDate(X)
log D
set SecondDate to (current date) -- = system date
set ListDiff to DateDiff(D, CD) -- returns {diff days, diff months, diff years}
log "Days = " & item 1 of ListDiff
log "Months = " & item 2 of ListDiff
log "Years = " & item 3 of ListDiff
on DateDiff(D1, D2) -- return list with difference in days, in months, in years
-- depending if differences is less than month, or less than year or higher than a year
if D1 > D2 then -- set DStart as oldest date
copy {D1, D2} to {Dend, DStart}
else
copy {D1, D2} to {DStart, Dend}
end if
return {(Dend - DStart) div days, (Dend - DStart) div (30 * days), (Dend - DStart) div (365 * days)}
end DateDiff
on ConvertDate(X) -- sub routine to convert string "english_month dayth/st, year" to real date
set MS to {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
set LW to every word of X
if (count of LW) is not 3 then return "" -- invalid format
set MI to 0 -- check month : should be in the list
repeat with I from 1 to 12
if item I of MS is item 1 of LW then set MI to I
end repeat
if MI is 0 then return "" -- the fisrt word is not in the list of months
try -- check day : it should be NNth of NNst
set DI to (text 1 thru -3 of item 2 of LW) as integer
end try
if not ((DI > 0) and (DI < 31)) then return "" -- invalid day
try -- check year
set YI to (item 3 of LW) as integer
end try
if not ((YI > 0) and (YI < 9999)) then return "" -- invalid year
return date ((DI & "/" & MI & "/" & YI) as string)
end ConvertDate
Sub-routine "DateDiff" bellow gives back a list of 3 difference values : in days, months and years.
Set X to MyDatefrom Safari
Set MyDate to ConvertDate(X)
set SecondDate to (current date) -- = system date
set ListDiff to DateDiff(D, CD) -- returns {diff days, diff months, diff years}
log "Days = " & item 1 of ListDiff
log "Months = " & item 2 of ListDiff
log "Years = " & item 3 of ListDiff
on DateDiff(D1, D2) -- return list with difference in days, in months, in years
-- depending if differences is less than month, or less than year or higher than a year
if D1 > D2 then -- set DStart as oldest date
copy {D1, D2} to {Dend, DStart}
else
copy {D1, D2} to {DStart, Dend}
end if
return {(Dend - DStart) div days, (Dend - DStart) div (30 * days), (Dend - DStart) div (365 * days)}
end DateDiff
on ConvertDate(X) -- copy your existing sub-routine
end ConvertDate
For instance if MyDate = Jan 20th 2016 and we are August 26th 2016, it will return {219, 7, 0} because différence is 216 days or 7 months (Jan to August) or 0 year (2016 both dates !).
I have a variable with a date table that looks like this
* table:
[day]
* number: 15
[year]
* number: 2015
[month]
* number: 2
How do I get the days between the current date and the date above? Many thanks!
You can use os.time() to convert your table to seconds and get the current time and then use os.difftime() to compute the difference. see Lua Wiki for more details.
reference = os.time{day=15, year=2015, month=2}
daysfrom = os.difftime(os.time(), reference) / (24 * 60 * 60) -- seconds in a day
wholedays = math.floor(daysfrom)
print(wholedays) -- today it prints "1"
as #barnes53 pointed out could be off by one day for a few seconds so it's not ideal, but it may be good enough for your needs.
You can use the algorithms gathered here:
chrono-Compatible Low-Level Date Algorithms
The algorithms are shown using C++, but they can be easily implemented in Lua if you like, or you can implement them in C or C++ and then just provide Lua bindings.
The basic idea using these algorithms is to compute a day number for the two dates and then just subtract them to give you the number of days.
--[[
http://howardhinnant.github.io/date_algorithms.html
Returns number of days since civil 1970-01-01. Negative values indicate
days prior to 1970-01-01.
Preconditions: y-m-d represents a date in the civil (Gregorian) calendar
m is in [1, 12]
d is in [1, last_day_of_month(y, m)]
y is "approximately" in
[numeric_limits<Int>::min()/366, numeric_limits<Int>::max()/366]
Exact range of validity is:
[civil_from_days(numeric_limits<Int>::min()),
civil_from_days(numeric_limits<Int>::max()-719468)]
]]
function days_from_civil(y, m, d)
if m <= 2 then
y = y - 1
m = m + 9
else
m = m - 3
end
local era = math.floor(y/400)
local yoe = y - era * 400 -- [0, 399]
local doy = math.modf((153*m + 2)/5) + d-1 -- [0, 365]
local doe = yoe * 365 + math.modf(yoe/4) - math.modf(yoe/100) + doy -- [0, 146096]
return era * 146097 + doe - 719468
end
local reference_date = {year=2001, month = 1, day = 1}
local date = os.date("*t")
local reference_days = days_from_civil(reference_date.year, reference_date.month, reference_date.day)
local days = days_from_civil(date.year, date.month, date.day)
print(string.format("Today is %d days into the 21st century.",days-reference_days))
os.time (under Windows, at least) is limited to years from 1970 and up. If, for example, you need a general solution to also find ages in days for people born before 1970, this won't work. You can use a julian date conversion and subtract between the two numbers (today and your target date).
A sample julian date function that will work for practically any date AD is given below (Lua v5.3 because of // but you could adapt to earlier versions):
local
function div(n,d)
local a, b = 1, 1
if n < 0 then a = -1 end
if d < 0 then b = -1 end
return a * b * (math.abs(n) // math.abs(d))
end
--------------------------------------------------------------------------------
-- Convert a YYMMDD date to Julian since 1/1/1900 (negative answer possible)
--------------------------------------------------------------------------------
function julian(year, month, day)
local temp
if (year < 0) or (month < 1) or (month > 12)
or (day < 1) or (day > 31) then
return
end
temp = div(month - 14, 12)
return (
day - 32075 +
div(1461 * (year + 4800 + temp), 4) +
div(367 * (month - 2 - temp * 12), 12) -
div(3 * div(year + 4900 + temp, 100), 4)
) - 2415021
end
I recently encountered below scenario in drools. I want to know how to proceed with the rule design for this.
Class Emp{
beingDate:Date
endDate:Date
}
Rule to determine annual income for the employee based on the given dates:
For dates before 3/5/2003 the hourly rate is $3.5 and annual multiplier is 2100
For dates after 3/5/2003 the hourly rate changes every year (given data) and annual multiplier is 2092.
There might be scenarios where begin date is before 3/5/2003 and end date is after 3/5/2003.
What is the best way to design rules for this scenario.
Update: added an e.g. for more clarity
If the object is
empObj={
beginDate=10/8/2001,
endDate=5/10/2005
}
The rule should give the sum of below:
3.5 * (no. of days in 2001 starting 10/8/2001) / (total no. of days in 2001) * 2100
3.5 * 2100 ==> This is for year 2002
3.5 * (no. of days in 2003 before 3/5/2003) / (total no. of days in 2003) * 2100
(2003 hourly rate) * (no. of days in 2003 after 3/5/2003) / (total no. of days in 2003) * 2092 ==> note the change in yearly multiplier..
(2004 hourly rate) * 2092
(2005 hourly rate) * (no. of days in 2005 before 5/10/2005) / (total no. of days in 2005) * 2092
One way to do this is to have one rule per year. So it would look something like this
rule "2001"
when:
e : Emp( beginDate < "01-Jan-2002" )
then:
// 1. Get the number of days worked in 2001, probably easiest to do with some Java helper method
// 2. Calculate the sum
// 3. Add the sum to some Fact, could be the same Emp fact even
end
rule "2002"
when:
e : Emp( beginDate < "01-Jan-2003" )
then:
// As with 2001
end
The rest of the rules are very similar, just change the yearly multiplier accordingly. If you decide to use the Emp object to hold the sum, add method like
class Emp {
long sum = 0
void addToSum( long value ) { sum += value }
}
And in your RHS side call the method and update the object on each rule.
Hope this helps.