Процент рабочих дней в месяце

11

Учитывая год и месяц, определите процент рабочих дней в указанном месяце. Рабочие дни - с понедельника по пятницу, независимо от праздников и других особых вещей. Григорианский календарь используется.

вход

Год и месяц в формате ISO 8601 (ГГГГ-ММ). Год всегда состоит из четырех цифр, месяц всегда состоит из двух цифр. Данный год не будет до 1582 года.

Вывод

Выходные данные - это процент рабочих дней (согласно приведенному выше определению) в данном месяце, округленный до целого числа. Знак процента или дробные цифры не следуют.

Образец 1

Input                Output

2010-05              68

Образец 2

Input                Output

2010-06              73

Образец 3

Input                Output

1920-10              68

Образец 4

Input                Output

2817-12              68

Прошла неделя, ответ принят. Для любопытных, размеры заявок, которые мы получили в нашем конкурсе:

129 - Z shell
174 - VB.NET
222 - C
233 - C
300 - C

А также наши собственные (без рейтинга) решения:

  75 - PowerShell
  93 - Ruby
112 - Оболочка Борна

детеныш
источник
2
Я аспирант, так что ...echo 100
Amory
Даже аспиранты не могут избежать фундаментальных определений в своей работе. И я определил рабочие дни по-разному ;-)
Joey

Ответы:

4

64-битный Perl, 67 68

Perl 5.10 или более поздней версии, запустить с perl -E 'code here'илиperl -M5.010 filename

map{$d++,/^S/||$w++if$_=`date -d@ARGV-$_`}1..31;say int.5+100*$w/$d

Уступки к размеру кода:

  • чувствительный к локали: он считает рабочими днями дни, dateвыходные данные которых не начинаются с заглавной буквы S. Запустите, LC_ALL=Cесли есть сомнения.
  • вывод чистый и хорошо отформатированный, но на stderr есть «мусор» в месяцах короче 31. 2> /dev/nullесли расстроен.
  • почему-то моя версия dateсчитает 2817-12 недействительным месяцем. Кто знал, GNU новый апокалипсис из-за! Требуется 64-битная сборка dateдля дат после 2038 года. (Спасибо, Джои)
JB
источник
1
Очевидно это было отменено "Сифусом Хемесом" во время его правления. ссылка «Новая история Святой Библии»
Мартин Йорк,
1
Разве каждый год после 2038 года ломается? Тогда переключение на 64-битную сборку может помочь из-за некоторого умственного подхода к обработке даты ;-)
Joey
@ Джой, это именно так. Спасибо за совет!
JB
ДБ: Это было только предположение, и я на самом деле не ожидал, что ничего кроме C будет по-прежнему использовать только 32-битные целые числа, которые отсчитывают секунды со странной эпохи. Хотя, если честно, я поместил требование о датах> 2038 именно для этой цели ;-)
Joey
3

PHP - 135

Я сделал это на PHP, потому что у меня была похожая проблема, которую нужно было обработать несколько дней назад.

<?php $a=array(2,3,3,3,2,1,1);$t=strtotime($argv[1]);$m=date(t,$t);echo round((20+min($m-28,$a[date(w,strtotime('28day',$t))]))/$m*100)

(Несколько) Более разборчиво и без уведомлений об использовании констант в качестве строк:

<?php
date_default_timezone_set('America/New_York');
$additionalDays = array(2, 3, 3, 3, 2, 1, 1);
$timestamp = strtotime($argv[1]);
$daysInMonth = date('t', $timestamp);
$limit = $daysInMonth - 28;
$twentyNinthDayIndex = date('w', strtotime("+28 days", $timestamp));
$add = $additionalDays[$twentyNinthDayIndex];
$numberOfWorkDays = 20 + min($limit, $add);
echo round($numberOfWorkDays / $daysInMonth * 100);
?>

Это стало возможным благодаря очень простому алгоритму для подсчета количества рабочих дней в месяце: проверьте число дней недели 29-го, 30-го и 31-го (если эти даты существуют) и добавьте 20.

zneak
источник
Отличный алгоритм, плохой гольф. Используя современный PHP 5.3.5 и -R, этот подход может быть уменьшен до 86 байт (63,7%): $a="2333211";echo.5+min(-8+$m=date(t,$t=strtotime($argn)),20+$a[date(w,$t)])/$m*100|0; см.
Тит
80 байт:<?=.5+min(-8+$m=date(t,$t=strtotime($argn)),20+(5886>>date(w,$t)*2&3))/$m*100|0;
Тит
2

Python 152 персонажа

from calendar import*
y,m=map(int,raw_input().split('-'))
c=r=monthrange(y,m)[1]
for d in range(1,r+1):
 if weekday(y,m,d)>4:c-=1
print '%.f'%(c*100./r)
fR0DDY
источник
2

Bash + coreutils, 82 байта

f()(cal -NMd$1|sed -n "s/^$2.//p"|wc -w)
dc -e`f $1 "[^S ]"`d`f $1 S`+r200*r/1+2/p
Цифровая травма
источник
2

Windows PowerShell, 80

$x=$args;1..31|%{"$x-$_"|date -u %u -ea 0}|%{$a++
$b+=!!($_%6)}
[int]($b*100/$a)
детеныш
источник
Вы уверены, что [int]действительно раунды? Я склонен верить в это этажами.
zneak
@zneak: PowerShell не является C или производным от C языком. Он использует режим округления по умолчанию .NET, который является «округлением до ближайшего четного числа». Просто попробуйте это: Как [int]1.5и [int]2.5выход 2. Такое точное поведение часто вызывает проблемы в задачах, где необходимо поэтапное деление (которое затем требует дополнительного [Math]::Floor()), но в этом случае это не повредит, а «округление до четного» применяется только к числам, конец .5которых не может произойти здесь.
Джои
Если ты уверен, то я тебе верю. Я просто ожидал, что он будет работать как C #, и у меня нет машины с Windows, на которой можно тестировать дома.
zneak
@zneak: Нет, определенно не работает как в C #. Нечто подобное [int]в PowerShell обычно является скорее преобразованием, чем приведением :-). Такие вещи, как [int[]][char[]]'abc'также работа, которую вы не можете получить на многих других языках.
Джои
Necrobump но $input-> $argsсохраняет байт.
Веска
1

D: 186 символов

auto f(S)(S s){auto d=Date.fromISOExtendedString(s~"-28"),e=d.endOfMonth;int n=20;while(1){d+=dur!"days"(1);if(d>e)break;int w=d.dayOfWeek;if(w>0&&w<6)++n;}return rndtol(n*100.0/e.day);}

Более разборчиво:

auto f(S)(S s)
{
    auto d = Date.fromISOExtendedString(s ~ "-28"), e = d.endOfMonth;
    int n = 20;

    while(1)
    {
        d += dur!"days"(1);

        if(d > e)
            break;

        int w = d.dayOfWeek;

        if(w > 0 && w < 6)
            ++n;
    }

    return rndtol(n * 100.0 / e.day);
}
Джонатан М Дэвис
источник
1

Питон - 142

from calendar import*
y,m=map(int,raw_input().split('-'))
f,r=monthrange(y,m)
print'%.f'%((r-sum(weekday(y,m,d+1)>4for d in range(r)))*100./r)

Спасибо fR0DDY за бит календаря.

Juan
источник
1

Рубин, 124 119 111

require 'date'
e=Date.civil *$*[0].split(?-).map(&:to_i),-1
p ((e+1<<1..e).count{|d|d.cwday<6}*1e2/e.day).round

Требуется Ruby 1.9 из-за сплаттинга за год и месяц перед аргументом «день» -1 и ?-для "-". Для Ruby 1.8 мы должны добавить 2 символа:

require 'date'
e=Date.civil *$*[0].split('-').map(&:to_i)<<-1
p ((e+1<<1..e).count{|d|d.cwday<6}*1e2/e.day).round

Изменить : побрить пять символов на основе помощи @ Dogbert.
Изменить : побрить еще восемь символов на основе помощи @ steenslag.

Phrogz
источник
Почему вы назначаете дату для D?
Догберт
@ Dogbert Ой! Пережиток того времени, когда у меня было два Date.civils; Спасибо!
Phrogz
'-'может быть написано как ?-в Ruby 1.9
Dogbert
@ Догберт Ницца. Я тоже это добавлю. Я чувствую, что должен быть более короткий способ выбрать дни недели, но я еще не нашел его.
Phrogz
e + 1 << 1 в три раза короче, чем ee.day + 1
steenslag
1

PHP 5.2, 88 байт

Хотя я уже использовал решение zneak до 85 байт (я только что нашел еще один), вот мое собственное:
я сомневаюсь, что смогу выжать еще три байта здесь.

$a=_4444444255555236666304777411;echo$a[date(t,$t=strtotime($argn))%28*7+date(N,$t)]+67;

принимает входные данные из STDIN: запустить с echo <yyyy>-<mm> | php -nR '<code>'.

Строка $aотображает количество дней в месяце ( date(t)) и день недели первого дня месяца ( date(N): понедельник = 1, воскресенье = 7) с процентом рабочих дней - 67; strtotimeпреобразует ввод в метку времени UNIX; остальная часть кода выполняет хеширование.

+1 байт для старого PHP 5: заменить Nна wи $a=_...;на $a="...".
еще +3 байта для PHP 4: вставить .-1после $argn.

-5 байт для PHP 5.5 или более поздней (задним числом):
удалите все до echoи замените $aна "4444444255555236666304777411".

Titus
источник
Ну ... один байт: %7вместо %28.
Тит
0

Реболь - 118 113

w: b: 0 o: d: do join input"-01"while[d/2 = o/2][if d/7 < 6[++ w]++ b d: o + b]print to-integer round w / b * 100

Ungolfed:

w: b: 0 
o: d: do join input "-01"
while [d/2 = o/2] [
    if d/7 < 6 [++ w]
    ++ b
    d: o + b
]
print to-integer round w / b * 100
draegtun
источник
0

C #, 158 байт

s=>{var d=DateTime.Parse(s);int i=0,t=DateTime.DaysInMonth(d.Year,d.Month),w=0;while(i<t)w-=-(int)d.AddDays(i++).DayOfWeek%6>>31;return Math.Round(1e2*w/t);};

Анонимный метод, который возвращает необходимый процент.

Полная программа с ungolfed, комментируемым методом и контрольными примерами:

using System;

class WorkingDayPercentage
{
    static void Main()
    {
        Func <string, double> f =
        s =>
        {
            var d = DateTime.Parse(s);                      // extracts a DateTime object from the supplied string
            int i = 0,                                      // index variable
                t = DateTime.DaysInMonth(d.Year, d.Month),  // number of total number of days in the specified month
                w = 0;                                      // number of working days in the month

            while (i < t)                                   // iterates through the days of the month
                w -= -(int)d.AddDays(i++).DayOfWeek%6 >> 31;// d.AddDays(i) is the current day
                                                            // i++ increments the index variable to go to the next day
                                                            // .DayOfWeek is an enum which hold the weekdays
                                                            // (int)..DayOfWeek gets the days's index in the enum
                                                            // note that 6 is Saturday, 0 is Sunday, 1 is Monday etc.
                                                            // (int)DayOfWeek % 6 converts weekend days to 0
                                                            // while working days stay strictly positive
                                                            // - changes the sign of the positive numbers
                                                            // >> 31 extracts the signum
                                                            // which is -1 for negative numbers (working days)
                                                            // weekend days remain 0
                                                            // w -= substracts the negative numbers
                                                            // equivalent to adding their modulus

            return Math.Round(1e2 * w / t);                 // the Math.round function requires a double or a decimal
                                                            // working days and total number of days are integers
                                                            // also, a percentage must be returned
                                                            // multiplying with 100.0 converts the expression to a double
                                                            // however, 1e2 is used to shorten the code
        };

        // test cases:
        Console.WriteLine(f("2010-05")); // 68
        Console.WriteLine(f("2010-06")); // 73
        Console.WriteLine(f("1920-10")); // 68
        Console.WriteLine(f("2817-12")); // 68
    }
}

Альтернативная функция, которая добавляет отрицательные значения к числу рабочих дней, изменяя знак в возврате без дополнительных затрат в байтах:

s=>{var d=DateTime.Parse(s);int i=0,t=DateTime.DaysInMonth(d.Year,d.Month),w=0;while(i<t)w+=-(int)d.AddDays(i++).DayOfWeek%6>>31;return-Math.Round(1e2*w/t);};
adrianmp
источник
0

Oracle SQL, 110 байт

select round(100*sum(1-trunc(to_char(x+level-1,'d')/6))/sum(1))from dual,t connect by level<=add_months(x,1)-x

Он работает с предположением, что входные данные хранятся в таблице с использованием dateтипа данных, например

with t as (select to_date('2010-06','yyyy-mm') x from dual)
Доктор У Вит
источник
0

APL (Dyalog Unicode) , 55 байтов SBCS

Функция анонимного молчаливого префикса.

.5+100×2÷/(2 5)2{≢⍎⍕↓⍺↓¯2' ',cal' '@5⊢⍵⊣⎕CY'dfns'}¨⊂

 приложить дату, чтобы рассматривать ее в целом

(2 5)2{ Примените к нему следующую функцию, но с левыми аргументами [2,5]и 2:

⎕CY'dfns' скопировать библиотеку "dfns"

⍵⊣ отменить отчет в пользу даты

' '@5⊢ заменить 5-й символ ( -) пробелом

 выполнить это, чтобы получить список из двух элементов

cal вызвать функцию календаря на этом

' ', добавить к этому столбец пробелов

¯2⌽ поверните последние две колонки (суббота) вперед

⍺↓ удалить количество левых аргументов строк (2, заголовки) и столбцов (если указано; 5 = сб + вс)

 разбить матрицу на список строк

 формат (сплющивается с вставкой двойного интервала)

 выполнить (превращает номера оставшихся дней в плоский числовой список)

 подсчитать те

2÷/ разделить каждую пару (есть только одна)

100× умножить на сто

.5+ добавить половину

 этаж

Попробуйте онлайн!

Адам
источник