2014-05-28

Revit Deadline! Calculate Days Between Dates

I'd like to thank Joseph and Robin for their comments on Revit Calendar at revitforum.org. And a huge thank you goes to Oscar Lopez, curator of my morning ritual BIM WORLD. Seeing my own post there was like being on TV!

----
Edit 140618:
I'm a long time fan and follower of whatrevitwants. (As you can see from my top links list.)
It's an honor to be there! Thank you Luke!
----

at revitforum.org:

Me:
-You can use it in an evil or good way right now. Put a Comments label in it, adjust a date and place it on views in a work shared environment saying..
Good: "Hey don't forget the party!"
Evil: "Yes! That deadline was yesterday!

J:
-Yes, for Evil. I have also been thinking about doing some kind of project planning in Revit. For example if you make "Comment Objects" that a project manager or lead designer, with very little Revit experience, can place on drawings and fill in with date and actions they can be scheduled. This gives a clear overview of what needs to be done, by whom, when and if actions have been completed or not. It would be very useful to have Date and Time available as parameters in Revit so that actions approaching deadlines could be shown in ever brighter colours as the moment of death or glory approaches...

Me:

D_GEN_DEADLINE.rfa (Revit 2015)


The deadline family has 2 data entry parameters:
DateA and DateB. Family expects DateA to be earlier than DateB by default.

It has 2 check parameters:
DateAcheck and DateBcheck. These parameters show how the family interprets the dates we've entered.

And there's a txtMessage parameter with 6 possible outcomes.


While I was looking for a solution I've found Julian Day in wikipedia:
Julian Day is the continuous count of days since the beginning of the Julian Period used primarily by astronomers.

A count for each Date means I can subtract one from other to find the days in between. This would be easier than I've thought!

I also didn't want to have 6 entry parameters(y/m/d) for two dates so I've made 2 integer parameters which I parse into y/m/d and correct with DRV(drive) parameters and concatenate back into check parameters.

DateA (Integer Parameter)

year(at least 1 digit)month(2 digits)day(2 digits). So the earliest date you can enter is 10101.
(If you have a deadline before that let me know! :)

Parsing (Probably called something else!) done by getting the correct decimal places.

AyearParse = rounddown(DateA / 10000)
AmonthParse = rounddown(DateA / 100) - (AyearParse * 100)
AdayParse = DateA - (AyearParse * 10000) - (AmonthParse * 100)

I've used the same day / month / yearDRV(drive) parameters from the Revit Calendar.

Julian Day Number

You must compute first the number of years (y) and months (m) since March 1 −4800 (March 1, 4801 BC):


So be it!
Aa = rounddown((14 - AmonthDRV) / 12)
Ay = AyearDRV + 4800 - Aa
Am = AmonthDRV + (12 * Aa) - 3


AJulianDay = AdayDRV + rounddown(((153 * Am) + 2) / 5) + (365 * Ay) + rounddown(Ay / 4) - rounddown(Ay / 100) + rounddown(Ay / 400) - 32045

I've repeated the above steps for DateB.
BJulianDay - AJulianDay is the Droid we are looking for!

Usually I nest formulas when I believe they're working OK to reduce the number of parameters. For readability I didn't do it in this family.

Enjoy!.. Thanks for reading..

2014-05-26

Revit Calendar

Hi all. I've been blogging about Revit in Turkish for a time. (revitkutuphanesi.com) And I'll give it a try to do it in English here to interact with more people.

Why not start a blog by giving away something to the community? Here I represent to you the Revit Calendar Family:

D_GEN_CALENDAR.rfa (Revit 2015)

I was working on my LOD 300 Rebar Calculation add-in for Revit. For a week or two I was so concentrated on understanding and solving the problem, when it finished I've felt a little bit blank.
Had to do something Cool and pretty much useless to get over the boredom.


The calendar family has 3 data entry parameters. Day, Month, Year. If the Day parameter is Zero, it will basically show the monthly calendar.


If you enter a nice date (like my Son's b-day) it will add the day of the week to the title and highlight the date.


Although the family has some DRV (driving / correcting) parameters it doesn't cover negative years.
I'm sure it can be added to the DRV formulas but I didn't want to spend more than an a hour on it.
So any parameter "undersized  / oversized" value defaults to:

dayDRV= 0 /  maximum days this month has
monthDRV = 1 / 12
yearDRV =2 / ...

As time passed I can't remember the challenges in the exact order I had, but here they are:

LEAP YEAR (Leap Yes/No Parameter)

Had to figure out if the entered Year is a leap year. After searching the internet for a decent algorithm, luckily I've found this one in wikipedia.

if year is not divisible by 4 then common year
else if year is not divisible by 100 then leap year
else if year is not divisible by 400 then common year
else leap year

which I can rewrite in Revit as:

if(not(yearDRV / 4 = rounddown(yearDRV / 4)), 1 = 0, if(not(yearDRV / 100 = rounddown(yearDRV / 100)), 1 = 1, if(yearDRV / 400 = rounddown(yearDRV / 400), 1 = 1, 1 = 0)))

Revit does not have a modulo operator (finding the remainder of a division) in its syntax but you can use the roundown trick to find mod x = 0.

yearDRV / 4 = rounddown(yearDRV / 4)

If it can be divided to four without a remainder, it should be equal to the rounddown of the same division Right? Rest is adjusting the formula with ifs and nots..

DETERMINATION of THE FIRST DAY

To start the month I had to find out the location of the first day in the table. Again it was lovely to find out a page dedicated to this in wikipedia.

There were a lot of promising methods. First I thought using the "methods with tables" would be so cool but due to the time restrain I had to choose from one of the "Purely Mathematical Algorithms". I've chosen Zeller's Congruence


h is the day of the week (0 = Saturday, 1 = Sunday, 2 = Monday, ...)
q is the day of the month (I'm looking for the FirstDay = 1)
m is the month (3 = March, 4 = April, 5 = May, ..., 14 = February)
K the year of the century (year mod 100).
J is the century (For example, in 1995 the century would be 19, even though it was the 20th century.)

The formulas rely on the mathematician's definition of modulo division, which means that −2 mod 7 is equal to positive 5. Unfortunately, the way most computer languages implement the remainder function, −2 mod 7 returns a result of -2. So, to implement Zeller's congruence on a computer, the formulas should be altered slightly to ensure a positive numerator. The simplest way to do this is to replace − 2J by + 5J:


Zeller used decimal arithmetic, and found it convenient to use J and K in representing the year. But when using a computer, it is simpler to handle the modified year Y, which is Y - 1 during January and February:


And you can write it in Revit as: (zellerHdayFirst Integer Parameter)

FirstDay + rounddown(((if(monthDRV = 1, 13, if(monthDRV = 2, 14, monthDRV))) + 1) * 2.6) + yearDRV + rounddown(yearDRV / 4) + (6 * rounddown(yearDRV / 100)) + rounddown(yearDRV / 400) - (7 * rounddown((FirstDay + rounddown(((if(monthDRV = 1, 13, if(monthDRV = 2, 14, monthDRV))) + 1) * 2.6) + yearDRV + rounddown(yearDRV / 4) + (6 * rounddown(yearDRV / 100)) + rounddown(yearDRV / 400)) / 7))

THE MATRIX


In the matrix I have two kinds of Labels (some overlapping):

DAYS and GRAYS

DAYS are all we need. GRAYS are just the decorative ones showing values from the last / next month in.. well.. GRAY.

Zeller's Algorithm starts the week from Saturday with a value of zero  (0 = Saturday, 1 = Sunday, 2 = Monday, ...) I've done the same naming the Matrix Elements. So let's say d32 means:

d = it's a DAYS Label
3 =  Third Row
2 = Zeller Day Two which is Monday

Notice the first group of GRAYS start from g12 to g10. This Month has to start somewhere in the first week! and the last day that can be is Sunday (d11) so the GRAYS should end there.
As this group of GRAYS show the last days of the last month I needed to figure out the count of the days in the last month.

cntDAYSlast (Integer Parameter)

if(or(monthDRVlast = 1, monthDRVlast = 3, monthDRVlast = 5, monthDRVlast = 7, monthDRVlast = 8, monthDRVlast = 10, monthDRVlast = 12), 31, if(or(monthDRVlast = 4, monthDRVlast = 6, monthDRVlast = 9, monthDRVlast = 11), 30, if(Leap, 29, 28)))

Saying if the Last Month is
1,3,5,7,8,10,12 -> 31 days
else if
4,6,9,11 -> 30 days
else if
Leap Year -> 29
not anything above :) 28

Labels from g12 to g10 are counting down from that number before the first day of this month and g12v to g10v are controlling the visibility of these labels.

DAYS

We know the Zeller first day. (zellerHdayFirst)
And the count of the days in this month. (cntDAYS).
So we count the days of this month :)

Second part of the GRAYS (g52 to g61) just count from the end of this month. These Labels start from g52 because the earliest this month can start from is Monday (d12) and shortest this month can only be is 28 days.

SELECTED DATE

For the selected date instead of creating a matrix and playing with visibility I've placed a family where I can move with Revit dimensions. It would definitely give more options to have a Matrix but would have taken a longer time. (By the time I've got to this point I've started to realize I've got things to do. Just like now realizing it's 3 AM and  I've got to go to bed :)

zellerHdaySelected (Integer Parameter) Find the Zeller Day of the Selected Day
todayKX (Integer Parameter) Multiplier for the Column
todayKY (Integer Parameter) Multiplier for the Row
todayX (Length Parameter) Driving the Today Ring in X direction
todayY (Length Parameter) Driving the Today Ring in Y direction


Enjoy!.. Thanks for reading..