23 de dez. de 2008

Feliz Natal

while ( TheEarth.IsRunning() &&
ThereIsHope &&
DateTime.Today.Day.Equals(25) &&
DateTime.Today.Month.Equals(12))
{
Console.WriteLine("Feliz Natal");
}

6 de dez. de 2008

Feriados móveis

Estava abrindo minhas correspondências essa semana e achei um envelope do meu contador, que recebo todo ano em Dezembro, um calendário do ano seguinte (e um boleto bancário do 13º, óbvio).

Daí comecei a ver os feriados do ano que vem, e que são muitos.
Lembrei de uma coisa interessante, que nem todo mundo sabe: como calcular feriados móveis.

Apenas por curiosidade, os calendários são baseados em eventos astrológicos, e obviamente a mudança dos feriados móveis também é baseada em cálculos astrológicos.

Muita gente se pergunta porque o Carnaval sempre cai num dia diferente todo ano, as vezes até em outro mês.

A base de todos feriados móveis é a data da Páscoa.

Carnaval = Páscoa – 47 dias
Sexta Feira Santa = Páscoa – 2 dias
CorpusChristi =   Páscoa + 60 dias

A data também é móvel e é calculada através de uma fórmula muito doida.
Encontrei uns documentos antigos na minha pesquisa e fiz uma rotina em Visual Basic há muito tempo (no mínimo uns 10 anos).
Hoje resolvei convertê-la pra C# para utilizar em um sistema que estou trabalhando.

Vamos ao código:

static void Main(string[] args)
{
DateTime dtPascoa = DataPascoa(2009);
DateTime dtCarnaval = dtPascoa.AddDays(-47);
DateTime dtSextaSanta = dtPascoa.AddDays(-2);
DateTime dtCorpusChristi = dtPascoa.AddDays(60);
Console.WriteLine(string.Format("Páscoa: {0}", dtPascoa.ToLongDateString()));
Console.WriteLine(string.Format("Carnaval: {0}", dtCarnaval.ToLongDateString()));
Console.WriteLine(string.Format("Sexta-Feira Santa: {0}", dtSextaSanta.ToLongDateString()));
Console.WriteLine(string.Format("Corpus Christi: {0}", dtCorpusChristi.ToLongDateString()));
Console.ReadKey();
}
O código do cálculo da Páscoa
static DateTime DataPascoa(int nYear)
{
int nGold = 0;
int nCent = 0;
int nCorx = 0;
int nCorz = 0;
int nSunday = 0;
int nEpact = 0;
int nMoon = 0;
int nMonth = 0;
int nDay = 0;

// nGold, é o Golden number
nGold = (nYear % 19) + 1;

// nCent é o Século
nCent = (nYear / 100) + 1;

// nCorx é o número de anos em que o bissexto foi retirado para sincronizar
nCorx = ((3 * nCent) / 4 - 12);

// nCorz é uma correção para sincronizar a Páscoa com a órbita da Lua
nCorz = ((8 * nCent + 5) / 25 - 5);
nSunday = ((5 * nYear) / 4 - nCorx - 10);

// nEpact é a ocorrencia de uma Lua cheia
nEpact = ((11 * nGold + 20 + nCorz - nCorx) % 30);

if (nEpact < 0)
nEpact = nEpact + 30;

if (((nEpact == 25) && (nGold > 11)) || (nEpact == 24))
nEpact = nEpact + 1;

// Lua cheia - a n ésima lua de Março é o calendário da lua cheia
nMoon = 44 - nEpact;

if (nMoon < 21)
nMoon = nMoon + 30;

// Avança ao domingo
nMoon = (nMoon + 7 - ((nSunday + nMoon) % 7));

// obtém o mês e o dia
if (nMoon > 31)
{
nMonth = 4;
nDay = nMoon - 31;
}
else
{
nMonth = 3;
nDay = nMoon;
}

return new DateTime(nYear, nMonth, nDay);

}

Technorati Tags: ,,

5 de dez. de 2008

Download de arquivos dinâmicos

Download de arquivos gerados dinamicamente é uma rotina comum em muitos sites.

Já precisei fazer isso diversas vezes e o código normalmente se parece com isso:

Response.AddHeader("Content-Size", bytArq.Length.ToString());
Response.AddHeader("Content-Disposition", "attachment; filename=" +
NomeArquivo);
Response.OutputStream.Write(bytArq, 0, bytArq.Length);Response.End();
onde:
byteArq é um byte [] do conteudo do arquivo, sendo ele texto ou binário 
NomeArquivo é a o nome padrão sugerido pelo browser ao gravar o arquivo.
Até aí nenhuma novidade, mas vamos aos desafios.
Qual o problema do código acima ?
Se eu quisesse fazer o download do arquivo e logo em seguida atualizar algum campo na página, como por exemplo um label "Arquivo enviado com sucesso" ?

Com o código acima, não seria possível.


A linha Response.End(), interrompe a execução da página, e faz com que qualquer código logo após seja ignorado (pelo menos código que gere html de retorno).


Por quê essa linha é necessária ?


Porque sem ela o código no .cs seria executado e logo após o browser iria redenderizar a execução do aspx, o que faria com que o resultado gerado seria a combinação do arquivo mais o html, corrompendo o arquivo.


Na prática não é possível enviar 2 conteúdos ao browser simultaneamente, ou seja, ou eu envio o html, ou envio o arquivo.

Vamos aos workarounds:

E se ao invés de escrever os bytes do arquivo, eu abrisse uma janela, onde lá sim, escreveria o byte array, e na minha página principal continuaria meu código?

A solução seria:

pagina.aspx, onde faria a chamada do download do arquivo
download.aspx, um arquivo sem html no aspx


No código na pagina.aspx:
Um método para escrever abrir a página de download

void DownloadArquivo(Page p, byte[] bytArq, string NomeArquivo)
{
Session["downloadBytes"] = bytArq;
Session["downloadName"] = NomeArquivo;
System.Text.StringBuilder s = new System.Text.StringBuilder();
s.Append("\n<SCRIPT LANGUAGE='JavaScript'>\n");
s.Append("function download()");
s.Append("{\n");
s.Append(" window.open('download.aspx', 'Download', 'toolbar=no,
location=no, directories=no, status=yes, menubar=no, scrollbars=no,
resizable=no, titlebar=no, copyhistory=no, width=100, height=100, left=0,
top=0');\n"
);
s.Append("}\n");
s.Append("window.onload = download;\n");
s.Append("</SCRIPT>");
p.RegisterClientScriptBlock("download", s.ToString());
}


A chamada desse método é bem simples, em qualquer botão de processamento e download seria:

// chame seu metodo que gere o arquivo ou pegue-o do banco de dados 
byte
[] arq = GeraSeuArquivo();
DownloadArquivo(this, arq, "meuarquivo.ext");
lblMeuLabel.Text = "Pronto o arquivo foi enviado para o browser";

No código no download.aspx, apenas o minimo necessário

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="download.aspx.cs" Inherits="download" Title="Download..." %>

O código no codebehind dessa página teria apenas o método Page_Load()
protected void Page_Load(object sender, EventArgs e)
{
byte[] bytArq;
string NomeArquivo;
if (Session["downloadBytes"] != null)
{
bytArq = (byte[])Session["downloadBytes"];
if (bytArq.Length > 0)
{
NomeArquivo = (string)Session["downloadName"];
Session["downloadBytes"] = null;
Session["downloadName"] = null;
Response.AddHeader("Content-Disposition", "attachment; filename=" +
NomeArquivo);
Response.AddHeader("Content-Type", "application/force-download");
Response.AddHeader("Content-Type", "application/octet-stream");
Response.AddHeader("Content-Type", "application/download");
Response.AddHeader("Content-Description", "File Transfer");
Response.OutputStream.Write(bytArq, 0, bytArq.Length);
}
Response.End();
}

}

Como funciona a mágica:


A página irá gerar o arquivo (byte array) e renderizará um código javascript que abrirá um popup.


Antes de terminar a execução da pagina.aspx ela irá armazenar o conteudo desse arquivo na Session.


Ao termino da execução, o popup irá começar e executará o Page_Load


Nesse método, o download.aspx irá recuperar esse arquivo, removê-lo da Session e renderizá-lo no browser.

Simples e direto.

Limitações:


A solução funciona, mas tem suas limitações: como a transferência de conteúdo é feita através da Session, durante essa transferência, o servidor irá ocupar esse espaço de memória (mesmo que removendo logo em seguida), o que na prática quer dizer que se for transferido um arquivo muito grande, poderá ocorrer um OutOfMemory Exception.


Taí, duvido vocês testarem.

O porquê de um blog

Estava aqui pensando comigo, o porquê de se criar um blog e, conversando com meu colega Vinicius Canto, achei um motivo, e dos bons.
Backup das minhas idéias !
Show de bola.

Sempre fico doido quando quero fazer uma simples rotina que já fiz em outro projeto ou outra empresa e nunca lembro onde guardei aquele pedaço de código.
Agora sim, meus problemas acabaram !
Simplesmente vou guardar tudo online.

Estava querendo escrever há um bom tempo, mas nunca há tempo.
Já recebi convites para escrever para revistas, mas o problema, como sempre é o tempo.
A vantagem do blog nessa hora é justamente essa. Numa revista existe a "obrigatoriedade" de ter um artigo regular numa coluna, e num blog não, vou escrevendo quando der.
Sem contar que o alcance é maior.

Taí, gostei da idéia.