Porcentaje de días de trabajo en un mes.

11

Dado un año y un mes, averigüe el porcentaje de días de trabajo en dicho mes. Los días de trabajo son de lunes a viernes sin tener en cuenta los días festivos u otras cosas especiales. Se utiliza el calendario gregoriano.

Entrada

Un año y mes en formato ISO 8601 (AAAA-MM). El año siempre tiene cuatro dígitos, el mes siempre tiene dos dígitos. El año dado no será antes de 1582.

Salida

La producción es el porcentaje de días de trabajo (según la definición anterior) en el mes dado, redondeado a un número entero. No hay signos de porcentaje o dígitos fraccionarios.

Muestra 1

Input                Output

2010-05              68

Muestra 2

Input                Output

2010-06              73

Muestra 3

Input                Output

1920-10              68

Muestra 4

Input                Output

2817-12              68

Ha pasado una semana, se ha aceptado una respuesta. Para los curiosos, el tamaño de las presentaciones que obtuvimos en nuestro concurso:

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

Además de nuestras propias soluciones (sin clasificar):

  75 - PowerShell
  93 - Ruby
112 - Shell Bourne

Joey
fuente
2
Soy un estudiante graduado, así que ...echo 100
Amory
Incluso los estudiantes de posgrado no pueden escapar de las definiciones fundamentales en su línea de trabajo. Y definí los días de trabajo de manera diferente ;-)
Joey

Respuestas:

4

Perl de 64 bits, 67 68

Perl 5.10 o posterior, ejecuta con perl -E 'code here'operl -M5.010 filename

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

Concesiones al tamaño del código:

  • sensible a la configuración regional: cuenta como días de trabajo los días cuya dateproducción no comienza con una S mayúscula. Ejecutar en LC_ALL=Ccaso de duda.
  • la salida es pura y bien formateada, pero hay "basura" en stderr en meses inferiores a 31. 2> /dev/nullsi está molesto.
  • Por alguna razón, mi versión de date2817-12 considera un mes no válido. ¡Quién sabía, el nuevo apocalipsis de GNU se debe! Requiere una compilación de 64 bits datepara fechas posteriores a 2038. (Gracias Joey)
JB
fuente
1
Aparentemente fue abolido por "Siphous Hemes" durante su gobierno. ref "Una nueva historia de la Santa Biblia"
Martin York
1
¿Se rompe cada año después de 2038? Luego, cambiar una compilación de 64 bits podría ayudar debido a cierta confusión mental con el manejo de la fecha ;-)
Joey
@ Joey, eso es exactamente. ¡Gracias por el consejo!
JB
JB: Era solo una suposición y en realidad no esperaba que nada más allá de C siguiera usando únicamente enteros de 32 bits que cuenten segundos desde una época extraña. Aunque, para ser honesto, puse el requisito sobre fechas> 2038 allí exactamente para este propósito ;-)
Joey
3

PHP - 135

Lo hice en PHP porque tuve un problema similar que tratar hace unos días.

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

(Algo) Más legible, y sin avisos sobre las constantes que se utilizan como cadenas:

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

Esto es posible gracias a un algoritmo muy simple para calcular la cantidad de días de trabajo en un mes: verifique el día de la semana del 29, 30 y 31 (si esas fechas existen), y agregue 20.

zneak
fuente
Gran algoritmo, mal golf. Usando PHP 5.3.5 contemporáneo -R, y este enfoque puede reducirse a 86 bytes (63.7%): $a="2333211";echo.5+min(-8+$m=date(t,$t=strtotime($argn)),20+$a[date(w,$t)])/$m*100|0; vea los pasos de golf.
Titus
80 bytes:<?=.5+min(-8+$m=date(t,$t=strtotime($argn)),20+(5886>>date(w,$t)*2&3))/$m*100|0;
Tito el
2

Python 152 Personajes

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

Windows PowerShell, 80

$x=$args;1..31|%{"$x-$_"|date -u %u -ea 0}|%{$a++
$b+=!!($_%6)}
[int]($b*100/$a)
Joey
fuente
¿Estás seguro de que [int]realmente redondea? Tiendo a creer que suena.
zneak
@zneak: PowerShell no es C ni un lenguaje derivado de C. Utiliza el modo de redondeo predeterminado de .NET que es »redondear al entero par más cercano«. Solo pruébelo: ambos [int]1.5y [int]2.5ceda el paso 2. Este comportamiento exacto a menudo causa problemas en tareas en las que es necesaria la división en pisos (que luego requiere un extra [Math]::Floor()), pero en este caso no duele y »redondear a incluso« solo se aplica a números que terminan en los .5que no puede suceder aquí.
Joey
Si estás seguro, entonces te creo. Solo esperaba que funcionara como C #, y no tengo ninguna máquina de Windows para probar en casa.
zneak
@zneak: No, definitivamente no funciona como en C #. Algo así como [int]en PowerShell suele ser más una conversión que un elenco :-). Cosas como [int[]][char[]]'abc'también funcionan que no puedes conseguir en muchos otros idiomas.
Joey
Necrobump pero $input-> $argsguarda un 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);}

Más legible:

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

Gracias a fR0DDY por el bit de calendario.

Juan
fuente
1

Rubí, 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

Requiere Ruby 1.9 debido a las salpicaduras del año y mes antes del argumento -1 "día" y ?-para "-". Para Ruby 1.8, debemos agregar 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 : afeita cinco personajes según la ayuda de @ Dogbert.
Editar : afeita ocho personajes más según la ayuda de @ steenslag.

Phrogz
fuente
¿Por qué estás asignando Fecha a D?
Dogbert
@Dogbert Whoops! Restantes de una época en que tenía dos Date.civils; ¡Gracias!
Phrogz
'-'podría escribirse como ?-en Ruby 1.9
Dogbert
@Dogbert Nice. Yo también lo arrojaré. Siento que debe haber una forma más corta de elegir los días de la semana, pero aún no la he encontrado.
Phrogz
e + 1 << 1 es tres más corto que ee.day + 1
steenslag
1

PHP 5.2, 88 bytes

Aunque ya utilicé la solución de Zneak hasta 85 bytes (acabo de encontrar uno más), aquí está la mía:
dudo que pueda exprimir otros tres bytes aquí.

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

toma entrada de STDIN: Ejecutar con echo <yyyy>-<mm> | php -nR '<code>'.

La cadena $aasigna los días por mes ( date(t)) y el día de la semana del primer día del mes ( date(N): lunes = 1, domingo = 7) al porcentaje de días de trabajo-67; strtotimeconvierte la entrada a una marca de tiempo UNIX; el resto del código hace el hashing.

+1 byte para PHP 5 anterior: reemplazar Ncon wy $a=_...;con $a="...".
Otros +3 bytes para PHP 4: insertar .-1después $argn.

-5 bytes para PHP 5.5 o posterior (es posterior al desafío):
elimine todo antes echoy reemplácelo $acon "4444444255555236666304777411".

Titus
fuente
Bueno ... un byte: en %7lugar de %28.
Tito el
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

Sin golf:

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
fuente
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 devuelve el porcentaje requerido.

Programa completo con método no comentado y comentado y casos de prueba:

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

Función alternativa, que agrega valores negativos al número de días hábiles, cambiando el signo en la devolución sin costo 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
fuente
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

Funciona suponiendo que los datos de entrada se almacenan en una tabla al usar dateel tipo de datos, por ejemplo

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

APL (Dyalog Unicode) , SBCS de 55 bytes

Función de prefijo tácito anónimo.

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

 adjunte la fecha para tratarlo como un todo

(2 5)2{...  aplique la siguiente función sobre eso, pero con los argumentos de la izquierda [2,5]y 2:

⎕CY'dfns' copie la biblioteca "dfns"

⍵⊣ descartar el informe a favor de la fecha

' '@5⊢ reemplazar el quinto carácter ( -) con un espacio

 ejecutar eso para obtener una lista de dos elementos

cal llamar a la función de calendario en ese

' ', anteponer una columna de espacios a ese

¯2⌽ rotar las dos últimas columnas (sábado) al frente

⍺↓ suelte el número de argumento de la izquierda de filas (2, encabezados) y columnas (si se especifica; 5 = Sat + Sun)

 dividir la matriz en una lista de líneas

 formato (se aplana con inserción de doble espacio)

 ejecutar (convierte los días restantes en una lista numérica plana)

 cuenta esos

2÷/ divide cada par (solo hay uno)

100× multiplicar por cien

.5+ agregue la mitad

 suelo

Pruébalo en línea!

Adán
fuente