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


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)

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.


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

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

2

Windows PowerShell, 80

$x=$args;1..31|%{"$x-$_"|date -u %u -ea 0}|%{$a++
$b+=!!($_%6)}
[int]($b*100/$a)

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

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.


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.


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".


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

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

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)

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!


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.