Porcentagem de dias úteis em um mês

11

Dado um ano e um mês, descubra a porcentagem de dias úteis nesse mês. Os dias úteis são de segunda a sexta-feira, sem considerar feriados ou outras coisas especiais. O calendário gregoriano é usado.

Entrada

Um ano e mês no formato ISO 8601 (AAAA-MM). O ano sempre tem quatro dígitos, o mês sempre tem dois dígitos. O ano indicado não será antes de 1582.

Resultado

A saída é a porcentagem de dias úteis (de acordo com a definição acima) no mês especificado, arredondada para um número inteiro. Nenhum sinal de porcentagem ou dígitos fracionários seguem.

Amostra 1

Input                Output

2010-05              68

Amostra 2

Input                Output

2010-06              73

Amostra 3

Input                Output

1920-10              68

Amostra 4

Input                Output

2817-12              68

Uma semana se passou, uma resposta foi aceita. Para os curiosos, os tamanhos das inscrições que recebemos em nosso concurso:

129 - Casca em Z
174 - VB.NET
222 - C
233 - C
300 - C

Assim como nossas próprias soluções (sem classificação):

  75 - PowerShell
  93 - Ruby
112 - Casca de Bourne

Joey
fonte
2
Eu sou um estudante de graduação, então ...echo 100
Amory
Mesmo os estudantes de pós-graduação não podem escapar das definições fundamentais em sua linha de trabalho. E eu definido dias de trabalho de forma diferente ;-)
Joey

Respostas:

4

Perl de 64 bits, 67 68

Perl 5.10 ou posterior, execute com perl -E 'code here'ouperl -M5.010 filename

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

Concessões ao tamanho do código:

  • sensível ao código do idioma: conta como dias úteis os dias cuja datesaída não começa com uma letra S. maiúscula LC_ALL=C.
  • a saída é pura e bem formatada, mas há "lixo" no stderr nos meses inferiores a 31. 2> /dev/nullse chateado.
  • por alguma razão, minha versão do date2817-12 considera um mês inválido. Quem sabia, o novo apocalipse do GNU é devido! Requer uma compilação de 64 bits datepara datas após 2038. (Obrigado Joey)
JB
fonte
11
Aparentemente, foi abolido por "Siphous Hemes" durante seu governo. ref "Uma nova história da Bíblia Sagrada"
Martin York
11
Todo ano depois de 2038 é quebrado? Em seguida, mudar ta 64-bit de construção podem ajudar devido a algum braindead-ness com a manipulação de data ;-)
Joey
@ Joey é exatamente isso. Obrigado pela dica!
JB
JB: Foi apenas um palpite e, na verdade, eu não esperava que nada além de C ainda usasse números inteiros de 32 bits que contam segundos desde uma época estranha. Embora, para ser honesto, eu coloquei a exigência sobre datas> 2038 lá exatamente para este fim ;-)
Joey
3

PHP - 135

Eu fiz isso em PHP porque tinha um problema semelhante para tratar há alguns dias.

<?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)

(Um pouco) Mais legível e sem avisos sobre constantes sendo usadas como seqüências de caracteres:

<?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);
?>

Isso é possível graças a um algoritmo muito simples para calcular o número de dias úteis em um mês: verifique o dia da semana nos dias 29, 30 e 31 (se essas datas existirem) e adicione 20.

zneak
fonte
Ótimo algoritmo, pouco golfe. Utilizando o PHP 5.3.5 e contemporâneo -R, essa abordagem pode ter até 86 bytes (63,7%): $a="2333211";echo.5+min(-8+$m=date(t,$t=strtotime($argn)),20+$a[date(w,$t)])/$m*100|0; Veja as etapas de golfe.
Titus
80 bytes:<?=.5+min(-8+$m=date(t,$t=strtotime($argn)),20+(5886>>date(w,$t)*2&3))/$m*100|0;
Titus
2

Python 152 caracteres

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
fonte
2

Bash + coreutils, 82 bytes

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
Trauma Digital
fonte
2

Windows PowerShell, 80

$x=$args;1..31|%{"$x-$_"|date -u %u -ea 0}|%{$a++
$b+=!!($_%6)}
[int]($b*100/$a)
Joey
fonte
Tem certeza de que [int]realmente ronda? Eu tenderia a acreditar no chão.
zneak
@ zneak: O PowerShell não é um idioma derivado de C. Ele usa o modo de arredondamento padrão do .NET, que é »redondo para o número inteiro par mais próximo«. Apenas tente: ambos [int]1.5e [int]2.5ceder 2. Esse comportamento exato geralmente causa problemas nas tarefas em que é necessária a divisão do piso (o que exige um extra [Math]::Floor()), mas neste caso não dói e "arredondar para o mesmo" se aplica apenas a números que terminam e .5que não podem acontecer aqui.
Joey
Se você tem certeza, eu acredito em você. Só esperava que funcionasse como C # e não tenho nenhuma máquina Windows para testar em casa.
Zneak 28/02
@ zneak: Não, definitivamente não funciona como em C #. Algo parecido [int]com o PowerShell geralmente é mais uma conversão do que um elenco :-). Coisas como [int[]][char[]]'abc'também funcionam que você não pode trabalhar em muitos outros idiomas.
Joey
Necrobump mas $input-> $argssalva um byte.
Veskah
1

D: 186 caracteres

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);}

Mais legivelmente:

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);
}
Jonathan M Davis
fonte
1

Python - 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)

Obrigado a fR0DDY pelo bit do calendário.

Juan
fonte
1

Ruby, 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

Requer Ruby 1.9 devido a splatting do ano e mês antes do argumento -1 "dia" e ?-para "-". Para o Ruby 1.8, devemos adicionar 2 caracteres:

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

Editar : barbeie cinco caracteres com base na ajuda de @ Dogbert.
Editar : barbeie mais oito caracteres com base na ajuda de @ steenslag.

Phrogz
fonte
Por que você está atribuindo Data a D?
Dogbert
@Dogbert Whoops! Ressaca de uma época em que eu tinha dois Date.civils; obrigado!
Phrogz 28/02
'-' poderia ser escrito como ?- em Ruby 1.9
Dogbert
@Dogbert Nice. Vou jogar isso também. Sinto que deve haver uma maneira mais curta de escolher os dias da semana, mas ainda não a encontrei.
Phrogz 28/02
e + 1 << 1 é três mais curto do que ee.day + 1
steenslag
1

PHP 5.2, 88 bytes

Embora eu já tenha jogado a solução do zneak com menos de 85 bytes (acabei de encontrar mais uma), aqui está a minha:
duvido que possa extrair mais três bytes aqui.

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

recebe entrada de STDIN: Corra com echo <yyyy>-<mm> | php -nR '<code>'.

A sequência $amapeia os dias por mês ( date(t)) e o dia da semana do primeiro dia do mês ( date(N): segunda-feira = 1, domingo = 7) para a porcentagem de dias úteis-67; strtotimeconverte a entrada em um registro de data e hora UNIX; o restante do código faz o hash.

+1 byte para o PHP 5 mais antigo: Substitua Npor we $a=_...;por $a="...".
outros +3 bytes para o PHP 4: insira .-1depois $argn.

-5 bytes para o PHP 5.5 ou posterior (pós-desafio):
remova tudo antes echoe substitua $apor "4444444255555236666304777411".

Titus
fonte
Bem ... um byte: em %7vez de %28.
Titus
0

Rebol - 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
fonte
0

C #, 158 bytes

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);};

Método anônimo que retorna a porcentagem necessária.

Programa completo com métodos não comentados e comentados e casos de teste:

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
    }
}

Função alternativa, que adiciona valores negativos ao número de dias úteis, alterando o sinal no retorno sem custo adicional de bytes:

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
fonte
0

Oracle SQL, 110 bytes

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

Ele trabalha com a suposição de que os dados de entrada são armazenados em uma tabela ao usar o datetipo de dados, por exemplo,

with t as (select to_date('2010-06','yyyy-mm') x from dual)
Dr. Y Wit
fonte
0

APL (Dyalog Unicode) , SBCS de 55 bytes

Função de prefixo tácito anônimo.

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

 incluir data para tratá-lo como um todo

(2 5)2{ Aplique a seguinte função nisso, mas com os argumentos à esquerda [2,5]e 2:

⎕CY'dfns' copie a biblioteca "dfns"

⍵⊣ descartar o relatório em favor da data

' '@5⊢ substitua o quinto caractere ( -) por um espaço

 execute isso para obter lista de dois elementos

cal chame a função de calendário nesse

' ', anexar uma coluna de espaços a essa

¯2⌽ gire as duas últimas colunas (sábado) para a frente

⍺↓ soltar o número do argumento esquerdo de linhas (2, cabeçalhos) e colunas (se especificado; 5 = Sáb + Dom)

 dividir matriz em lista de linhas

 formato (aplana com inserção de espaçamento duplo)

 execute (transforma os números restantes do dia em uma lista numérica plana)

 contabilizar aqueles

2÷/ divida cada par (existe apenas um)

100× multiplicar por cem

.5+ adicione meio

 chão

Experimente online!

Adão
fonte
0

Perl 6 , 78 bytes

{round 100*@_.grep(6>*.day-of-week)/@_}o{Date.new("$_-01")...^*.month-*.month}

Experimente online!

Brincadeira
fonte