Пришла мне как-то в голову идея изобразить производственный календарь в двоичном новогоднем виде. Напрограммил макрос в экселе http://nzdr.ru/files/hny2012.xls, потом это дело в картинку сконвертил. Позже переписал на php, а после и вовсе на яваскрипте. Получилось как-то так:
Код на VBA для Microsoft Excel:
'календарь Sub main() Dim iYear As Integer, iMonth As Integer, iDay As Integer Dim iDayOfWeek As Integer, iDaysInMonth As Integer Dim iOffset As Integer, iColor As Integer, j 'чистим ячейки Cells.ClearContents Cells.Interior.ColorIndex = xlNone ActiveWindow.DisplayGridlines = False 'задаём год iYear = 2012 For iMonth = 1 To 12 iDayOfWeek = Weekday(DateSerial(iYear, iMonth, 1), vbMonday) If (iDayOfWeek > 5) Then iOffset = iDayOfWeek - 4 Else iOffset = iDayOfWeek + 3 End If iDaysInMonth = Day(DateSerial(iYear, iMonth + 1, 0)) For iDay = 1 To iDaysInMonth iColor = 10 If isOffDay(DateSerial(iYear, iMonth, iDay)) Then iColor = 3 If isPreHolyday(DateSerial(iYear, iMonth, iDay)) Then iColor = 6 For j = 0 To 5 If Mid(Right("00000" & Bin(CLng(iDay)), 5), j + 1, 1) = "1" Then Cells(iOffset + iDay - 1, iMonth * 6 + j - 4).Interior.ColorIndex = iColor Next j Next iDay Next iMonth End Sub 'является ли указанная дата праздничным днём Function isHolyday(dtDate As Date) As Boolean Dim y As Integer y = Year(dtDate) isHolyday = False 'Российские праздники '1, 2, 3, 4 и 5 января — Новогодние каникулы; If dtDate >= DateSerial(y, 1, 1) And dtDate <= DateSerial(y, 1, 5) Then isHolyday = True '7 января — Рождество Христово; If dtDate = DateSerial(y, 1, 7) Then isHolyday = True '23 февраля — День защитника Отечества; If dtDate = DateSerial(y, 2, 23) Then isHolyday = True '8 марта — Международный женский день; If dtDate = DateSerial(y, 3, 8) Then isHolyday = True '1 мая — Праздник Весны и Труда; If dtDate = DateSerial(y, 5, 1) Then isHolyday = True '9 мая — День Победы; If dtDate = DateSerial(y, 5, 9) Then isHolyday = True '12 июня — День России; If dtDate = DateSerial(y, 6, 12) Then isHolyday = True '4 ноября — День народного единства. If dtDate = DateSerial(y, 11, 4) Then isHolyday = True End Function 'исключения из "стандартной схемы" выходных и рабочих дней Function isExcept(dtDate As Date) As Boolean isExcept = False 'обрабатываем переносы в 2012 году: 'на Новый год и Рождество If dtDate = DateSerial(2012, 1, 6) Or dtDate = DateSerial(2012, 1, 9) Then isExcept = True 'на 8 марта If dtDate = DateSerial(2012, 3, 9) Or dtDate = DateSerial(2012, 3, 11) Then isExcept = True 'на 1 мая If dtDate = DateSerial(2012, 4, 28) Or dtDate = DateSerial(2012, 4, 30) Then isExcept = True 'на 12 июня If dtDate = DateSerial(2012, 6, 9) Or dtDate = DateSerial(2012, 6, 11) Then isExcept = True 'перед Новым Годом If dtDate = DateSerial(2012, 12, 29) Or dtDate = DateSerial(2012, 12, 31) Then isExcept = True End Function 'является ли указанная дата выходным днем Function isOffDay(dtDate As Date) As Boolean 'выходные обычно суббота, воскресенье и праздники; остальные дни - рабочие Select Case Weekday(dtDate, vbMonday) Case 6, 7 'суббота, воскресенье isOffDay = True Case Else isOffDay = False If isHolyday(dtDate) Then isOffDay = True End Select 'если день находится в исключениях, значит инвертируем выходной/рабочий If isExcept(dtDate) Then isOffDay = Not isOffDay End Function 'предпраздничные дни (рабочие, но на час короче) Function isPreHolyday(dtDate As Date) As Boolean isPreHolyday = False If dtDate = DateSerial(2012, 2, 22) _ Or dtDate = DateSerial(2012, 3, 7) _ Or dtDate = DateSerial(2012, 4, 28) _ Or dtDate = DateSerial(2012, 5, 8) _ Or dtDate = DateSerial(2012, 6, 9) _ Or dtDate = DateSerial(2012, 12, 29) _ Then isPreHolyday = True End Function 'перевод десятичного числа в двоичное Function Bin(ByVal x As Long) As String Dim s As String, d As Long, f As Boolean If x < 0 Then s = "1": f = True d = &H40000000 Do If x And d Then s = s & "1": f = True Else If f Then s = s & "0" End If If d = 1 Then Exit Do d = d \ 2 Loop Bin = s End Function
Код на PHP:
<php? /* А сам код я куда-то проглаголил. Ну и, в общем, не нужен он, так как ниже приведена реализация на javascript. */ ?>
Чуток CSS:
.c0,.c1,.c2,.c3 {width:5px;height:5px;float:left;margin:1px 1px 0 0;} .c0{background-color:transparent !important;} .c1{background-color:#F00;} .c2{background-color:#080;} .c3{background-color:#FF0;} .n_bin_cal {min-width:700px;}
Код на javascript:
function inArray(arr,val) {var i=arr.length; while (i--) {if (arr[i].valueOf()==val.valueOf()) return true;} return false;} //праздники function isHolyday(d) { var year=d.getFullYear(); return inArray(new Array( new Date(year,0,1),new Date(year,0,5), new Date(year,4,1), new Date(year,0,2),new Date(year,0,7), new Date(year,4,9), new Date(year,0,3),new Date(year,1,23),new Date(year,5,12), new Date(year,0,4),new Date(year,2,8), new Date(year,10,4)),d); } //дни - исключения (переносы) function isExcept(d) { return inArray(new Array( new Date(2016,0,6), new Date(2016,0,8),new Date(2016,1,22), new Date(2016,1,27),new Date(2016,2,7),new Date(2016,4,2), new Date(2016,4,3), new Date(2016,5,13)),d); } //предпраздничные дни (рабочие, но на час короче) function isPreHolyday(d) {return inArray(new Array(new Date(2016,1,20),new Date(2016,10,3)),d);} //является ли указанная дата выходным днём function isOffDay(d) { switch (d.getDay()) { case 0: case 6: isOff=true; break; //субботы и воскресенья по умолчанию выходные default: isOff=false; if (isHolyday(d)) isOff=true; //работаем, если не праздник } if (isExcept(d)) isOff=!isOff; //инвертируем переносы return isOff; } function draw(y) { var bin=[1,2,4,8,16,32]; //это степени двойки, чтобы не считать var days_in_month=[31,28,31,30,31,30,31,31,30,31,30,31]; //дней в месяцах if ((y%4==0&&y%100!=0)||(y%400==0)) {days_in_month[1]=29;} //если високосный год, то в феврале 29 дней //заполняем матрицу - 72 (12 месяцев по 6 знакомест) столбцов на 37 строк // (31 день в месяце + сдвиг внутри недели до 6 дней) var a=new Array(72); for (i=0;i<a.length;i++) {a[i] = new Array(37);} for (i=0;i<a.length;i++) for (j=0;j<a[i].length;j++) a[i][j]=0; for (m=0;m<12;m++) { //перебираем месяцы w=(new Date(y,m,1)).getDay(); //вычисляем первый день месяца (для сдвига) k=1; //счётчик дней в месяце for (i=w;i<w+days_in_month[m];i++) { d=new Date(y,m,k); //текущая дата в календаре //номер класса в css: 0-пустышка, 1-красный, 2-зелёный, 3-жёлтый c=isOffDay(d)?1:2; if (isPreHolyday(d)) c=3; for (b=0;b<6;b++) a[m*6+(5-b)][i]=(k&bin[b])?c:0; k++; } } //заполнили матрицу кодами классов, рисуем раскрашенную таблицу document.write("<center><table class='noborder n_bin_cal'><tr width=100% align=center><td width=100% align=center>"); for (i=0;i<37;i++) { document.write("<div style='clear:both;'>"); for (j=0;j<72;j++) {document.write("<div class='c"+a[j][i]+"'></div>");} document.write("</div>"); } document.write("</td></tr></table></center>"); } draw(2016);
А на 2018 год пришлось немного допилить скрипт - заполнил массивы переносами и немного увеличил квадратики :). Получилось вот так:
<style>.c0,.c1,.c2,.c3 {width:8px;height:8px;float:left;margin:1px 1px 0 0;}</style>
<script type="text/javascript"> <!-- //дни - исключения (из-за переносов) function isExcept(d) { return inArray(new Array( new Date(2018,0,8), new Date(2018,2,9), new Date(2018,3,30), new Date(2018,4,2), new Date(2018,5,11), new Date(2018,11,31) ),d); } //предпраздничные дни (рабочие, но на час короче) function isPreHolyday(d) {return inArray(new Array( new Date(2018,1,22), new Date(2018,2,7), new Date(2018,3,28), new Date(2018,4,8), new Date(2018,5,9), new Date(2018,11,29) ),d); } draw(2018); //--> </script>
<script type="text/javascript"> <!-- //дни - исключения (из-за переносов) function isExcept(d) { return inArray(new Array( new Date(2019,0,8), new Date(2019,4,2), new Date(2019,4,3), new Date(2019,4,10), ),d); } //предпраздничные дни (рабочие, но на час короче) function isPreHolyday(d) {return inArray(new Array( new Date(2019,1,22), new Date(2019,2,7), new Date(2019,3,30), new Date(2019,4,8), new Date(2019,5,11), new Date(2019,11,31) ),d); } draw(2019); //--> </script>
<script type="text/javascript"> <!-- //дни - исключения (из-за переносов) function isExcept(d) { return inArray(new Array( new Date(2020,0,6), new Date(2020,0,8), new Date(2020,1,24), new Date(2020,2,9), new Date(2020,4,4), new Date(2020,4,5), new Date(2020,4,11), ),d); } //предпраздничные дни (рабочие, но на час короче) function isPreHolyday(d) {return inArray(new Array( new Date(2020,3,30), new Date(2020,4,8), new Date(2020,5,11), new Date(2020,10,3), new Date(2019,11,31) ),d); } draw(2020); //--> </script>
<script type="text/javascript"> <!-- //дни - исключения (из-за переносов) function isExcept(d) { return inArray(new Array( new Date(2022,0,6), new Date(2022,2,7), new Date(2022,4,2), new Date(2022,4,3), new Date(2022,4,10), new Date(2022,5,13), ),d); } //предпраздничные дни (рабочие, но на час короче) function isPreHolyday(d) {return inArray(new Array( new Date(2022,1,22), new Date(2022,2,5), new Date(2022,10,3), ),d); } draw(2022); //--> </script>
В этом году решил добавить ещё номер года над календариком :) Пришлось немного расширить массивчик степеней:
var bin=[1,2,4,8,16,32,64,128,256,512,1024,2048]; //это степени двойки, чтобы не считать
и добавить чуток кода в функцию draw():
//заголовок года в двоичном виде document.write("<div style='display:table; margin:0 auto; text-align:center; padding-bottom:10px;'>"); for (i=11;i>=0;i--) { document.write("<div class='c"+((y&bin[i])?1:2)+"'></div>"); if (i%4==0) {document.write("<div class='c0'></div>");} } document.write("</div>");
Ну и переменная добавочка в этом году выглядит вот так:
<script type="text/javascript"> <!-- //дни - исключения (из-за переносов) function isExcept(d) { return inArray(new Array( new Date(2023,0,6), new Date(2023,1,24), new Date(2023,4,8), ),d); } //предпраздничные дни (рабочие, но на час короче) function isPreHolyday(d) {return inArray(new Array( new Date(2023,1,22), new Date(2023,2,7), new Date(2023,10,3), ),d); } draw(2023); //--> </script>
Кстати, любые правки кода отражаются и на отрисовке предыдущих годов. Код-то работает на всю страничку один. Так что все предыдущие годы отрисовываются, как и последний, и истории уже не увидеть :(
В этом году много всяких переносов, поэтому исключений больше.
<script type="text/javascript"> <!-- //дни - исключения (из-за переносов) function isExcept(d) { return inArray(new Array( new Date(2024,3,27), new Date(2024,3,29), new Date(2024,3,30), new Date(2024,4,10), new Date(2024,10,2), new Date(2024,11,28), new Date(2024,11,30), new Date(2024,11,31), ),d); } //предпраздничные дни (рабочие, но на час короче) function isPreHolyday(d) {return inArray(new Array( new Date(2024,1,22), new Date(2024,2,7), new Date(2024,4,8), new Date(2024,5,11), new Date(2024,10,2), ),d); } draw(2024); //--> </script>