Imprimir año en formato calendario (ejercicio)
Ya es viernes! así que algo entretenido.
Siguiendo el espíritu de esta pregunta:
http://www.javamexico.org/blogs/jsrivero22/crear_un_calendario
El ejercicio es imprimir el calendario del año así:
2015 January February March Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5 6 7 4 5 6 7 8 9 10 8 9 10 11 12 13 14 8 9 10 11 12 13 14 11 12 13 14 15 16 17 15 16 17 18 19 20 21 15 16 17 18 19 20 21 18 19 20 21 22 23 24 22 23 24 25 26 27 28 22 23 24 25 26 27 28 25 26 27 28 29 30 31 29 30 31 April May June Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 1 2 3 4 1 2 1 2 3 4 5 6 5 6 7 8 9 10 11 3 4 5 6 7 8 9 7 8 9 10 11 12 13 12 13 14 15 16 17 18 10 11 12 13 14 15 16 14 15 16 17 18 19 20 19 20 21 22 23 24 25 17 18 19 20 21 22 23 21 22 23 24 25 26 27 26 27 28 29 30 24 25 26 27 28 29 30 28 29 30 31 July August September Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 1 2 3 4 1 1 2 3 4 5 5 6 7 8 9 10 11 2 3 4 5 6 7 8 6 7 8 9 10 11 12 12 13 14 15 16 17 18 9 10 11 12 13 14 15 13 14 15 16 17 18 19 19 20 21 22 23 24 25 16 17 18 19 20 21 22 20 21 22 23 24 25 26 26 27 28 29 30 31 23 24 25 26 27 28 29 27 28 29 30 30 31 October November December Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5 4 5 6 7 8 9 10 8 9 10 11 12 13 14 6 7 8 9 10 11 12 11 12 13 14 15 16 17 15 16 17 18 19 20 21 13 14 15 16 17 18 19 18 19 20 21 22 23 24 22 23 24 25 26 27 28 20 21 22 23 24 25 26 25 26 27 28 29 30 31 29 30 27 28 29 30 31
(nota esta es la salida del comando cal -y
en Un*x)
Estaba pensando que debería de ser sencillo usando el paquete java.time y el método: LocalDate.getField(ChronoField.ALIGNED_WEEK_OF_YEAR)
Hasta ahorita mi razonamiento llegó a:
- Hacer 4 loops ( tres meses cada loop)
- Dentro hacer 5 loops y agregar N semanas así imprimir semana 1, 6 y 10 en el primer renglón
luego semana 2, 7, y 11 en el segundo etc.
- Saliendo del cicle interno y volviendo al externo saltarme 10 semanas para llegar a la semana 16 y repetir
Y ahí me quede. pseudo
for j to 5 {
printf ( week(i*j ) ) // enero, abril, jul, oct,
printf ( week(i*j ) + 4 ) // feb, mayo, ago, nov
printf ( week(i*j ) +10 ) // mar, jun ,sep, dic
}
// insert magic here so next time is 16,
}
Mi cerebro dejó de pensar cuando vi que este año tiene 53 semanas y los demás 52.
Se me ocurrió que esta bueno como ejercicio.
- OscarRyz's blog
- Inicie sesión o regístrese para enviar comentarios
Comentarios
Aproximación
No se ve exactamente igual, pero cumple con el layout..
import java.util.Locale;
class MyCal {
public static void main(String... args) {
Calendar c = Calendar.getInstance();
String [] daysOfWeek = { "Dom","Lun", "Mar", "Mie", "Jue", "Vie", "Sab" };
for (int i = Calendar.JANUARY; i < Calendar.DECEMBER; i+=3) {
c.set(Calendar.MONTH, i);
System.out.print("\t\t"+ c.getDisplayName(Calendar.MONTH, Calendar.LONG,Locale.getDefault())+ " ");
c.set(Calendar.MONTH, i+1);
System.out.print("\t\t\t\t\t"+ c.getDisplayName(Calendar.MONTH, Calendar.LONG,Locale.getDefault())+ " ");
c.set(Calendar.MONTH, i+2);
System.out.print("\t\t\t\t\t"+ c.getDisplayName(Calendar.MONTH, Calendar.LONG,Locale.getDefault())+ " ");
System.out.println(" \n");
for(int d=0;d<daysOfWeek.length*3; d++){
System.out.print(daysOfWeek[d%daysOfWeek.length] + "\t");
}
System.out.println(" \n");
Calendar m1 = Calendar.getInstance();
m1.set(Calendar.MONTH, i);
m1.set(Calendar.DATE, 1);
Calendar m2 = Calendar.getInstance();
m2.set(Calendar.MONTH, i+1);
m2.set(Calendar.DATE, 1);
Calendar m3 = Calendar.getInstance();
m3.set(Calendar.MONTH, i+2);
m3.set(Calendar.DATE, 1);
for(int nweeks=0;nweeks<5;nweeks++){
for(int it=0;it<21;it++){
if(it<7)
System.out.print(obtenerFecha(m1,i,(it%7)+1)+"\t");
else if(it<14)
System.out.print(obtenerFecha(m2, i+1,(it%7)+1)+"\t");
else
System.out.print(obtenerFecha(m3, i+2,(it%7)+1)+"\t");
}
System.out.println();
}
System.out.print("\n" );
}
}
public static String obtenerFecha(Calendar c, int mont,int dow){
int day = 0;
if(c.get(Calendar.MONTH)!=mont){
return " ";
}else if(c.get(Calendar.DAY_OF_WEEK)!=dow){
return " ";
}else{
day = c.get(Calendar.DATE);
c.add(Calendar.DATE, 1);
}
return " "+day;
}
}
Código del comando "cal"
Si acaso alguien desea ver el código del comando
cal
(Linux), puede encontrarlo en:http://git.kernel.org/cgit/utils/util-linux/util-linux.git/tree/misc-utils/cal.c
Ceylon
Ahí les va en Ceylon, usando el módulo
ceylon.time
. Queda exactamente igual, solamente falta que el nombre del mes empiece en mayúscula.today, date
}
import ceylon.time.base {
monthOf, Month, sunday
}
shared void run() {
"El día en que generamos el calendario."
value hoy = today();
"Los trimestres (4 secuencias de 3 meses cada una)"
value quarters = [ for (m in (1..12).partition(3)) m.collect(monthOf) ];
"Imprime los encabezados de mes."
void monthHeaders([Month+] months) {
for (mo in months) {
process.write("``mo.string.pad(20)`` ");
}
process.writeLine();
for (i in 1..3) {
for (d in sunday..saturday) {
process.write("``d.string.initial(2)`` ");
}
process.write(" ");
}
process.writeLine();
}
"Imprime la semana indicada del mes indicado.
La primera semana empieza en 0."
void printWeek(Integer num, Month mo) {
"El primer día del mes."
value day0 = date(hoy.year, mo, 1);
"La diferencia entre el domingo y el día de la semana
del primer día del mes."
value diff = day0.dayOfWeek.offset(sunday);
"El último día del mes."
value lastDay = mo.numberOfDays(hoy.leapYear);
"El primer día de la semana indicada."
value firstDayOfWeek = num*7-diff+1;
for (i in firstDayOfWeek:7) {
process.write(i > lastDay || i < 1 then " "
else "``i.string.padLeading(2)`` ");
}
process.write(" ");
}
for (q in quarters) {
monthHeaders(q);
for (w in 0:6) {
for (mo in q) {
printWeek(w, mo);
}
process.writeLine();
}
}
}
EDIT Los nombres de los días de la semana ya no están en código duro.
Más "ceylónico"
Una versión más "ceylónica": menos ciclos explícitos, más comprensiones, un let por ahí...
today, date
}
import ceylon.time.base {
monthOf, Month, sunday,
saturday
}
shared void run() {
"El día en que generamos el calendario."
value hoy = today();
"Los trimestres (4 secuencias de 3 meses cada una)"
value quarters = [ for (m in (1..12).partition(3)) m.collect(monthOf) ];
"Imprime los encabezados de mes."
String monthHeaders([Month+] months) =>
" ".join { for (mo in months) mo.string.pad(20) };
String weekHeaders([Month+] months) =>
" ".join { for (mo in months)
" ".join { for (d in sunday..saturday) d.string.initial(2) }
};
"Imprime la semana indicada del mes indicado.
La primera semana empieza en 0."
String weekDays(Integer num, Month mo) => let (
day0 = date(hoy.year, mo, 1),
diff = day0.dayOfWeek.offset(sunday),
lastDay = mo.numberOfDays(hoy.leapYear),
weekStart = num*7-diff+1) " ".join { for (i in weekStart:7)
if (i > lastDay || i < 1) then " " else i.string.padLeading(2)
};
for (q in quarters) {
print(monthHeaders(q));
print(weekHeaders(q));
for (w in 0:6) {
print(" ".join { for (mo in q) weekDays(w, mo) });
}
}
}
Ultimo
Esa segunda versión en Ceylon me gustó mucho más que la primera. No sé si estrictamente hablando se puede decir que es "programación funcional", pero no me importa eso realmente; creo que el código es entendible, y algo curioso es que "a pesar" de que Ceylon es un lenguaje de tipado estático, apenas y se ven tipos mencionados (tres en los tipos de retorno de las funciones, aunque bien podría decir
function
porque son locales, y los tipos de parámetros de esas mismas tres funciones).La función
weekDays
es una sola expresión: el uso delet
me permitió convertir todas las declaraciones y sentencias en una sola expresión usando las declaraciones hechas en ellet
.Y otra cosa: este programa es tan flexible, que con sólo cambiar el parámetro del
partition
, se imprime el calendario en otro layout: si ponen 4 en vez de 3, se imprimen 4 columnas de meses en vez de 3; igual funciona si ponen 2, o 6...Bueno pues fue mucho más
Bueno pues fue mucho más complicado de lo que pensé inicialmente y luego no me dí tiempo.
Al final querer implementar usando fuerza bruta me salió más complicado pero aquí está el resultado.
import java.time.format.TextStyle;
import java.util.Locale;
class Calendar {
public static void main(String ... args) {
String [] monthNames = new String[12];
String [] dayNames = new String[7];
int dowi = 0;
Locale locale = new Locale("es", "MX");
// primero de enero
LocalDate day = LocalDate.of(LocalDate.now().getYear(), 1, 1);
// matriz de 12 meses x 5 semanas cada mes x 7 dias a la semana
int[][][] a = new int[12][6][7];
// llenar la matriz
for (int i = 0 ; i < a.length; i++ ) {
Month current = day.getMonth();
monthNames[i] = current.getDisplayName(TextStyle.FULL, locale);
for (int j = 0 ; day.getMonth() == current ; j++ ) {
for (int k = 0 ; k < 7 && day.getMonth() == current ; k++ ) {
DayOfWeek dayOfWeek = day.getDayOfWeek();
int dow = dayOfWeek.getValue();
if( dayNames[dow == 7?0:dow] == null ) {
dayNames[dow == 7?0:dow] =
dayOfWeek.getDisplayName(TextStyle.FULL, locale).substring(0,2);
}
if ( k == dow || (k == 0 && dow == 7) ) {
a[i][j][k] = day.getDayOfMonth();
day = day.plusDays(1);
} else {
continue;
}
}
}
}
// fill a matrix with the expected layout
int [][] layout = new int[24][21];
int li = 0;
int lj = 0;
for( int i = 0 ; i < a.length; i+=3 ) {
for( int j = 0 ; j < a[i].length; j++ ) {
for( int k = 0 ; k < a[i][j].length; k++ ) {
layout[li][lj] = a[i][j][k];
layout[li][lj+7] = a[i+1][j][k];
layout[li][lj+14] = a[i+2][j][k];
lj++;
}
lj = 0;
li++;
}
}
// days of week header
StringBuilder dowb = new StringBuilder(" ");
for ( String dowa : dayNames ) {
dowb.append(dowa);
dowb.append(" ");
}
String daysHeader = dowb.toString() +dowb.toString() + dowb.toString();
// months header
StringBuilder monthsHeader = new StringBuilder(" ");
int k = 0;
String[] mh = new String[4];
for( int i = 0 ; i < monthNames.length ; i++){
String current = monthNames[i];
for ( int j = 0 ; j < 10 - current.length() / 2; j++ ) {
monthsHeader.append(' ');
}
monthsHeader.append(current);
for ( int j = 0 ; j < 9 - current.length() / 2; j++ ) {
monthsHeader.append(' ');
}
monthsHeader.append(" ");
if ( i > 0 && (i+1) % 3 == 0) {
mh[k++] = monthsHeader.toString();
monthsHeader = new StringBuilder(" ");
}
}
k = 0;
System.out.printf(" "+(day.getYear()-1));
for ( int i = 0 ; i < layout.length ; i++ ) {
for ( int j = 0; j < layout[i].length ; j++ ) {
if ( i % 6 == 0 && j == 0) {
System.out.printf("%n%s%n%s%n", mh[k++],daysHeader);
}
if ( j > 0 && j % 7 == 0 ){
System.out.print(" ");
}
int d = layout[i][j];
System.out.printf("%3s", d == 0 ? " ": d );
}
System.out.println();
}
}
}
Salida:
enero febrero marzo
do lu ma mi ju vi sá do lu ma mi ju vi sá do lu ma mi ju vi sá
1 2 3 1 2 3 4 5 6 7 1 2 3 4 5 6 7
4 5 6 7 8 9 10 8 9 10 11 12 13 14 8 9 10 11 12 13 14
11 12 13 14 15 16 17 15 16 17 18 19 20 21 15 16 17 18 19 20 21
18 19 20 21 22 23 24 22 23 24 25 26 27 28 22 23 24 25 26 27 28
25 26 27 28 29 30 31 29 30 31
abril mayo junio
do lu ma mi ju vi sá do lu ma mi ju vi sá do lu ma mi ju vi sá
1 2 3 4 1 2 1 2 3 4 5 6
5 6 7 8 9 10 11 3 4 5 6 7 8 9 7 8 9 10 11 12 13
12 13 14 15 16 17 18 10 11 12 13 14 15 16 14 15 16 17 18 19 20
19 20 21 22 23 24 25 17 18 19 20 21 22 23 21 22 23 24 25 26 27
26 27 28 29 30 24 25 26 27 28 29 30 28 29 30
31
julio agosto septiembre
do lu ma mi ju vi sá do lu ma mi ju vi sá do lu ma mi ju vi sá
1 2 3 4 1 1 2 3 4 5
5 6 7 8 9 10 11 2 3 4 5 6 7 8 6 7 8 9 10 11 12
12 13 14 15 16 17 18 9 10 11 12 13 14 15 13 14 15 16 17 18 19
19 20 21 22 23 24 25 16 17 18 19 20 21 22 20 21 22 23 24 25 26
26 27 28 29 30 31 23 24 25 26 27 28 29 27 28 29 30
30 31
octubre noviembre diciembre
do lu ma mi ju vi sá do lu ma mi ju vi sá do lu ma mi ju vi sá
1 2 3 1 2 3 4 5 6 7 1 2 3 4 5
4 5 6 7 8 9 10 8 9 10 11 12 13 14 6 7 8 9 10 11 12
11 12 13 14 15 16 17 15 16 17 18 19 20 21 13 14 15 16 17 18 19
18 19 20 21 22 23 24 22 23 24 25 26 27 28 20 21 22 23 24 25 26
25 26 27 28 29 30 31 29 30 27 28 29 30 31
Quien diría que mayo de este año tiene 6 semanas?
Versión ligeramente más
pfff aparentemente soy masoquista. Versión ligeramente más legible al crear un objeto y poner variables como instancias de clases y extraer métodos de instancia.
import java.time.format.TextStyle;
import java.util.Locale;
class Calendar {
private LocalDate day;
private int[][][] a;
private String [] monthNames = new String[12];
private String [] dayNames = new String[7];
private Locale locale = new Locale("es", "MX");
private String[] mh;
private String daysHeader;
/**
* lanza la app
*/
public static void main(String ... args ) {
Calendar c = new Calendar();
c.main();
}
/**
* main
*/
void main() {
// primero de enero
day = LocalDate.of(LocalDate.now().getYear(), 1, 1);
a = new int[12][6][7];
day = fillYear();
daysHeader = getWeekDaysHeader(dayNames);
mh = getMonthsHeader(monthNames);
printCalendar(fillLayout());
}
/**
*
* Llena 12 meses con 6 semanas cada uno y 7 días por semana.
* los días inexistentes en ese mes se llenan con 0
*/
private LocalDate fillYear() {
for (int i = 0 ; i < a.length; i++ ) {
Month current = day.getMonth();
monthNames[i] = current.getDisplayName(TextStyle.FULL, locale);
for (int j = 0 ; day.getMonth() == current ; j++ ) {
for (int k = 0 ; k < 7 && day.getMonth() == current ; k++ ) {
DayOfWeek dayOfWeek = day.getDayOfWeek();
int dow = dayOfWeek.getValue();
int zeroIfSunday = dow == 7 ? 0 : dow;
if( dayNames[zeroIfSunday] == null ) {
dayNames[zeroIfSunday] =
dayOfWeek.getDisplayName(TextStyle.FULL, locale).substring(0,2);
}
if ( k == dow || (k == 0 && dow == 7) ) {
a[i][j][k] = day.getDayOfMonth();
day = day.plusDays(1);
} else {
continue;
}
}
}
}
return day;
}
/**
* Fill a matrix with the expected layout
* Iterates the original months matrix
* and puts three days at a time
* in a 24x21 matrix
*/
private int[][] fillLayout() {
int [][] layout = new int[24][21];
int li = 0;
int lj = 0;
for( int i = 0 ; i < a.length; i+=3 ) {
for( int j = 0 ; j < a[i].length; j++ ) {
for( int k = 0 ; k < a[i][j].length; k++ ) {
layout[li][lj] = a[i][j][k];
layout[li][lj+7] = a[i+1][j][k];
layout[li][lj+14] = a[i+2][j][k];
lj++;
}
lj = 0;
li++;
}
}
return layout;
}
/**
* Actually prints the calendar by iterating the 24x21 matrix adding headers when needed.
*/
private void printCalendar(int[][] layout) {
int l = 0;
System.out.printf(" "+(day.getYear()-1));
for ( int i = 0 ; i < layout.length ; i++ ) {
for ( int j = 0; j < layout[i].length ; j++ ) {
if ( i % 6 == 0 && j == 0) {
System.out.printf("%n%s%n%s%n", mh[l++],daysHeader);
}
if ( j > 0 && j % 7 == 0 ){
System.out.print(" ");
}
int d = layout[i][j];
System.out.printf("%3s", d == 0 ? " ": d );
}
System.out.println();
}
}
/**
* Get the months header for the calendar
* each line contains 3 months.
*/
private String[] getMonthsHeader(String[] monthNames) {// months header
StringBuilder monthsHeader = new StringBuilder(" ");
int k = 0;
String[] mh = new String[4];
for( int i = 0 ; i < monthNames.length ; i++){
String current = monthNames[i];
for ( int j = 0 ; j < 10 - current.length() / 2; j++ ) {
monthsHeader.append(' ');
}
monthsHeader.append(current);
for ( int j = 0 ; j < 9 - current.length() / 2; j++ ) {
monthsHeader.append(' ');
}
monthsHeader.append(" ");
if ( i > 0 && (i+1) % 3 == 0) {
mh[k++] = monthsHeader.toString();
monthsHeader = new StringBuilder(" ");
}
}
return mh;
}
/**
* A single line containing the abbreviated days of week
* for the calendar
*/
private String getWeekDaysHeader(String[] dayNames) {
StringBuilder dowb = new StringBuilder(" ");
for ( String dowa : dayNames ) {
dowb.append(dowa);
dowb.append(" ");
}
return dowb.toString() +dowb.toString() + dowb.toString();
}
}
@ezamudio Intenté correr en
@ezamudio Intenté correr en el web runner y me pintó cremas con el import
Instalé ceylon con Homebrew y me dice que no reconocé el let.
Tú código es con una versión aún no liberada de Ceylon?
sí
En el web runner no lo puedes correr porque no permitimos importar módulos.
Y sí, el let es algo que saldrá con la versión 1.2, olvidé ese pequeño detalle...
Ok, intentando la versión
Ok, intentando la versión original. Al parecer le tengo que decir donde están los modulos/bibliotecas
Calendar.ceylon:1: error: package not found in imported modules: 'ceylon.time' (define a module and add module import to its module descriptor)
import ceylon.time {
^
module.ceylon
Crea un archivo module.ceylon junto a tu fuente con este código:
import ceylon.time "1.1.1";
}
Obvio con tu nombre de módulo, que debe coincidir con el directorio donde pusiste tus fuentes (no recuerdo si la versión 1.1 permite importar módulos cuando estás en el default).
Al parecer sunday no está
Al parecer
sunday
no está presente en Ceylon 1.1.0 y 1.1.1 aún no ha sido liberado.Podrías poner la salida de tu ejecución?
1.1.1
Es que 1.1.1 realmente ya va a ser 1.2.
Esta es la salida:
Re: 1.1.1
@ezamudio Sólo por curiosidad, ¿cuándo se liberaría la versión 1.2? Una fecha aproximada está bien.
soon
La fecha más aproximada que puedo dar es "este año"...
Soon™
Soon™
PFFFF
Estaba tratando de implementar esto con streams de Java 8, y usando la nueva funcionalidad de fechas tambien de J8, pero chale los streams de J8 no implementan particiones!
Domingo
Y para acabarla de chingar, el día 1 en java.time es lunes, no domingo...