Hora certa sincronizada no seu fuso e ferramentas de tempo. Relógio, cronômetro, temporizador, calendário com eventos, fusos horários e mais — em 13 idiomas, gratuito e sem rastreadores.
- Visão geral
- Ferramentas
- Idiomas e fusos
- Contas e eventos
- Identidade visual
- Como executar localmente
- Testes
- Publicação na Vercel
- Como funciona a sincronização de horário
- Limitações de precisão
- Dados de feriados
- Clima
- PWA e funcionamento offline
- Rotas e cabeçalhos
- Permissões do navegador
- Estrutura do repositório
- Autores
O GlobalTime (antes Brasil Time) é um site estático — HTML, CSS e JavaScript
puro, sem framework e sem etapa de build — publicado na Vercel com cinco Vercel
Functions: api/hora.js (sincronização do relógio), api/conta.js /
api/eventos.js (contas opcionais para guardar eventos do calendário),
api/feriados.js (feriados de outros países) e api/clima.js (clima,
previsão e busca de cidades).
Princípios:
- Precisão com transparência: o relógio é corrigido pelo horário do servidor. A página mostra só o estado ("Sincronizado"); latência, incerteza e fonte ficam na janela de detalhes, aberta ao clicar no estado. Quando não é possível sincronizar, isso é dito claramente.
- Interface enxuta: cada página mostra primeiro o essencial; ajustes, históricos e tabelas de apoio ficam em seções recolhíveis. A home mostra o relógio no centro, o lugar e o estado da sincronização discretos no topo, o próximo feriado e o acesso a todas as ferramentas.
- Localizado desde a primeira visita: idioma pelas preferências do navegador e hora no fuso do aparelho, ambos ajustáveis e salvos.
- Sem dependências em produção: nenhuma biblioteca de terceiros. Serviços externos: um Upstash Redis opcional (Marketplace da Vercel) para as contas e APIs públicas gratuitas, sem chave, consultadas pelas Functions (o navegador só fala com o próprio domínio): Nager.Date (feriados), Open-Meteo (clima e busca de cidades) e Nominatim/OpenStreetMap (nome do lugar a partir do GPS). As fontes (Inter e Plus Jakarta Sans, licença OFL) são auto-hospedadas.
- Privacidade: sem rastreadores. Preferências, voltas, alarmes, históricos e
eventos sem conta ficam no
localStorage; com conta, o servidor guarda só e-mail, senha protegida (scrypt) e os eventos.
| Rota | Ferramenta |
|---|---|
/ |
Hora certa sincronizada no fuso do aparelho (ou escolhido), digital ou analógica, com tela cheia em um clique (/tela-cheia redireciona para cá); horário de Brasília quando o fuso é outro; próximo feriado; clima agora |
/relogio-mundial |
Começa com poucas cidades a partir do seu fuso; adicione as suas (salvas no navegador). Diferença em relação a você e horário comercial |
/conversor |
Conversão de horário entre cidades/fusos (com tratamento de horário de verão) e planejador de reuniões |
/cronometro |
Cronômetro com voltas, melhor/pior volta, diferenças, cópia e exportação CSV |
/temporizador |
Contagem regressiva com alarme, notificação, progresso e tempo no título da aba |
/pomodoro |
Ciclos de foco/pausa configuráveis com histórico local |
/despertador |
Alarmes com data, repetição semanal, fuso e som |
/contagem |
Contagem regressiva para datas (Natal, Ano Novo, personalizadas) com link compartilhável |
/feriados |
Feriados nacionais, estaduais e municipais (capitais), pontos facultativos, feriadões e exportação .ics |
/calendario |
Calendário anual (padrão), mensal e semanal com feriados (Brasil com estados e capitais; outros países do relógio mundial pela Nager.Date), contagens e eventos pessoais (no navegador ou na conta) |
/clima |
Clima atual, próximas 48 h com gráfico, até 16 dias, chuva (chance, volume, períodos), vento, UV, nascer/pôr do sol; busca de cidades e localização do aparelho |
/sol |
Nascer e pôr do sol, meio-dia solar, crepúsculo e duração do dia |
/calculadoras |
Diferença entre datas, dias úteis com feriados, soma de dias, duração entre horários, conversão de unidades |
/timestamp |
Conversor de timestamp Unix (segundos/milissegundos) |
/docs |
Documentação em português e inglês (/sobre redireciona para cá) |
Atalhos de teclado (onde fazem sentido): Espaço iniciar/pausar, L volta, R zerar/reiniciar (com confirmação quando há risco de perder dados), Esc fechar janelas e sair de modos, ? lista de atalhos. Eles ficam desativados enquanto se digita.
- 13 idiomas: pt-BR, pt-PT, en, es, fr, de, it, nl, ru, ja, zh-CN, ko e ar
(árabe da direita para a esquerda), cada um com o próprio endereço:
/cronometro(pt-BR, na raiz),/en/cronometro,/pt-pt/cronometro,/zh-cn/cronometro… São HTMLs já traduzidos, gerados pornpm run gerarnas pastasen/,es/etc. (não edite: são sobrescritos), comlang,dir, título, descrição, canonical ehreflangpróprios. - Primeira visita: nas URLs em português, a Vercel redireciona (307) para a
versão no primeiro idioma do
Accept-Language, se houver. Buscadores rastreiam sem esse cabeçalho e veem cada URL como ela é. URLs com prefixo nunca são redirecionadas. - O seletor no rodapé abre a mesma página no idioma escolhido e salva a escolha
no cookie
gt_idioma(lido pela Vercel, tem prioridade sobre oAccept-Language) e emlocalStorage(gt:idioma). - 404 e offline respondem por qualquer endereço e são traduzidas no navegador (prefixo da URL, preferência salva ou idioma do navegador).
- A documentação longa existe em pt e en; nas demais línguas,
/xx/docsmostra o texto em inglês e aponta (canonical) para/en/docs. - Textos em
scripts/i18n/textos/:pt-BR.jsé a referência (com partes empt-BR/); cada idioma tem um arquivo com as mesmas chaves —testes/i18n.test.mjsconfere chaves, marcadores{x}e formas de plural. No HTML,data-i18n/data-i18n-attrsão preenchidos pornpm run gerar(em português na raiz e no idioma de cada pasta); nos scripts,t("chave", {...}), eurlLocal("/docs")para links no idioma atual. - Nomes de feriados brasileiros ficam em português (nomes próprios). A documentação existe em português e inglês.
- A hora é exibida no fuso do aparelho (
Intl), sem pedir localização; o usuário pode escolher outro na página inicial (scripts/nucleo/fuso-preferido.js). O fuso só muda a exibição: o instante vem sempre do relógio sincronizado.
- Sem conta, os eventos do calendário ficam no navegador.
- Com conta (e-mail e senha), ficam no Upstash Redis e aparecem em qualquer
aparelho. Senha com
scrypt(N=2^16, r=8, p=2, sal por conta; contas antigas são recalculadas no login); sessão em cookieHttpOnly,SameSite=Lax,Secure, 30 dias, guardada como hash; limite de tentativas por IP e por e-mail; pedidos que mudam dados exigem JSON e mesma origem; cada conta só acessa a própria chave. A conta pode ser excluída com a senha. - Não há recuperação de senha por e-mail (exigiria um serviço de e-mail).
- Sem as variáveis do Upstash,
/api/contarespondedisponivel: falsee o site segue só com eventos locais. No servidor local, um armazenamento em memória é usado automaticamente.
A nova identidade segue os princípios do site da LZdev:
monocromia, tipografia Plus Jakarta Sans (títulos) + Inter (texto e
números, com algarismos tabulares), cantos generosos, bordas discretas,
botões em pílula e a curva de animação cubic-bezier(.22, 1, .36, 1). Como a
LZdev é escura, a paleta foi invertida para fundo branco: a cor da marca é a
tinta quase preta (#0a0a0b) e o azul (#1d5bd8, versão mais escura do azul
da LZdev, para contraste AA) é o acento funcional. Há tema escuro opcional.
Marca: o símbolo é um globo com um relógio — a hora certa em qualquer
lugar. Os originais (preto e branco, 1254 px, fundo transparente) ficam em
logos/; deles saem os arquivos de assets/marca/ e assets/icones/:
simbolo-preto.png/simbolo-branco.png— símbolo do cabeçalho (tema claro / escuro);logo.png/logo-fundo-escuro.png— símbolo + nome, para fundo claro e escuro;logo-readme.png—logo.pngsobre cartão branco de cantos arredondados, para o topo deste README ficar legível também no tema escuro do GitHub;og.png— imagem de compartilhamento (1200×630);assets/icones/— logo branca em quadrado escuro: PNGs 96/192/512, versão maskable (dentro da zona segura) eapple-touch-icon;favicon.ico(16/32/48) na raiz.
Todos os tokens (cores, tipografia, espaçamentos, raios, sombra, transições)
estão no início de estilos/app.css. O sistema evita gradientes e usa uma
única sombra, reservada a menus e diálogos.
Requisito: Node.js 20 ou superior (nenhum pacote para instalar).
git clone https://github.com/LZdevelopers/Global-Time.git
cd Global-Time
npm run devAcesse http://localhost:3000. O servidor de desenvolvimento
(dev/servidor.mjs) imita a Vercel: URLs limpas, 404.html,
redirecionamentos e cabeçalhos do vercel.json, e executa api/hora.js.
Ver o site como um visitante de outro país: npm run demo -- paris
(lista: npm run demo -- ajuda, um lugar por idioma). Abre um Chrome com
perfil limpo, com o idioma do navegador, o fuso horário e a localização por
IP desse lugar: o site abre já redirecionado para o idioma, com o relógio, o
país do calendário, o próximo feriado e o clima de lá. Feche a janela para
encerrar.
Abrir os arquivos HTML direto pelo
file://não funciona: as páginas usam módulos JavaScript e caminhos absolutos (/estilos/...).
Em localhost o service worker só é registrado se você acessar uma vez com
?sw=1 (para não atrapalhar a edição dos arquivos).
Os blocos repetidos das páginas (metadados do <head>, cabeçalho, rodapé,
atalhos da home), as versões traduzidas, o sitemap.xml, o robots.txt, os
redirecionamentos de idioma do vercel.json e a lista de pré-cache do service
worker são mantidos a partir de dev/site.mjs:
npm run gerar # atualiza os arquivos
npm run checar # falha se algo estiver desatualizado (útil em CI)Não é um build: o resultado fica nos próprios arquivos, versionados. Edite o
conteúdo das páginas fora dos marcadores <!-- @head -->, <!-- @cabecalho -->
e <!-- @rodape -->.
npm test # testes unitários (node:test), sem dependências
npm run test:navegador # todas as páginas em Chrome/Edge headless
npm run test:pwa # service worker, pré-cache, offline e exclusão da API do cache- Unitários (
testes/): fusos e horário de verão, conversões de hora ambígua/inexistente, cálculo de offset e escolha da menor latência, fallback para cabeçalhoDatee relógio local, descarte de respostas antigas, detecção de salto de relógio, cronômetro e contagem regressiva (incluindo recarga da página), feriados (datas fixas, móveis, estaduais, municipais, feriadões,.ics), calculadoras e a funçãoapi/hora.js. - Navegador (
dev/testar-navegador.mjs): abre cada página em desktop (1366×900) e celular (390×844) via DevTools Protocol; falha com exceções, erros de console (inclusive violações de CSP), requisições quebradas, rolagem horizontal,<h1>ausente/duplicado, botões sem nome acessível e campos sem rótulo, e executa as verificações por página dedev/verificacoes-navegador.mjs. Capturas de tela vão para a pasta temporária do sistema (brasil-time-capturas). UseSO=/cronometro npm run test:navegadorpara uma página só (no Git Bash do Windows, prefixeMSYS_NO_PATHCONV=1). DefinaCHROME=se o navegador não for encontrado.FUSO=Europe/Lisbonsimula um aparelho em outro fuso,TEMA=darkabre as páginas no tema escuro eINTEIRA=1captura a página inteira.
- Antes do primeiro deploy, defina o domínio público em
dev/site.mjs(SITE_URL) — ou rodeSITE_URL=https://seu-dominio npm run gerar— e faça commit. Ele é usado emcanonical, Open Graph,sitemap.xmlerobots.txt. - Na Vercel: Add New → Project → importe o repositório.
- Framework Preset: Other
- Build Command: (vazio)
- Output Directory: (vazio — raiz do repositório)
- Deploy. Os arquivos de
api/viram as Functions/api/hora,/api/contae/api/eventos(api/_lib/não vira rota); overcel.jsonconfigura URLs limpas, redirecionamentos e cabeçalhos.dev/etestes/não são publicados (.vercelignore). - Contas (opcional): em Storage / Marketplace, crie um Upstash
Redis (plano gratuito) e conecte ao projeto. A Vercel injeta
KV_REST_API_URLeKV_REST_API_TOKEN(também aceitamosUPSTASH_REDIS_REST_URL/UPSTASH_REDIS_REST_TOKEN). Faça um novo deploy. Sem isso, tudo funciona e os eventos ficam só no navegador.
Para atualizar: rode npm test e npm run checar, faça commit e push — a
Vercel publica a cada push na branch de produção. Com a CLI: vercel (prévia)
e vercel --prod.
Depois do deploy, confira:
curl -i https://SEU-DOMINIO/api/hora # 200, {"timestamp":...}, Cache-Control: no-store
curl -i -X POST https://SEU-DOMINIO/api/hora # 405
curl -I https://SEU-DOMINIO/cronometro # 200 (URL limpa)
curl -I https://SEU-DOMINIO/paginas/cronometro.html # 308 → /cronometro
curl -I https://SEU-DOMINIO/nao-existe # 404 com a página personalizada
curl -s https://SEU-DOMINIO/api/conta # {"disponivel":true|false,"conta":null}
curl -I https://brasil-time.vercel.app/feriados # 308 → https://global-time.lzdev.com.br/feriados
curl -I https://SEU-DOMINIO/ | grep -i robots # nada (o domínio oficial é indexável)Implementação em scripts/nucleo/sincronizacao.js e api/hora.js.
GET /api/horadevolve{"timestamp": <ms Unix do servidor>}comCache-Control: no-store.- O navegador anota
performance.now()no envio (p0) e no recebimento (p1). Supõe-se que o servidor leu o relógio no meio da viagem, então em p1 a hora do servidor é estimada comotimestamp + (p1 − p0) / 2. - São feitas 5 medições na abertura (3 nas seguintes); vale a de menor latência, a menos afetada por filas e assimetria da rede.
- Entre sincronizações, a hora avança por
performance.now()(monotônico): ajustes no relógio do sistema não afetam a exibição. - Ressincroniza a cada 10 minutos, quando a aba volta a ficar visível (se a última sincronização tiver mais de 1 minuto), quando a rede volta e quando detecta um salto entre o relógio monotônico e o civil (suspensão do computador, ajuste manual do relógio).
- Cada rodada tem um número de sequência: uma resposta antiga que chegue depois de uma mais nova é descartada.
- A tela atualiza com
setTimeoutrecalculado para logo após cada virada de segundo (nãosetInterval), sem acumular atraso.
Hierarquia de fontes: (1) /api/hora; (2) cabeçalho HTTP Date de um
arquivo estático (resolução de 1 s — estado "Precisão limitada"); (3) relógio
do aparelho, sem correção ("Usando relógio do aparelho"). Sem rede, a última
correção válida continua sendo aplicada ("Offline · última sincronização").
- A incerteza exibida é metade do tempo de ida e volta da medição escolhida: o limite do erro causado pela rede, supondo que o servidor esteja certo. Não é garantia de precisão absoluta.
- O relógio do servidor da Vercel é disciplinado por NTP, mas não é uma fonte oficial de hora atômica. Para a Hora Legal Brasileira oficial, a referência é o Observatório Nacional.
- A rede pode ser assimétrica (ida mais lenta que a volta); isso gera um erro que nenhuma medição de ida e volta consegue detectar.
- A tela só pode mudar quando o navegador pinta um quadro (tipicamente a cada ~16 ms); abas em segundo plano têm temporizadores reduzidos pelo navegador.
Arquivos em scripts/dados/ (módulos JavaScript estáticos), calculados por
scripts/nucleo/feriados.js:
feriados-nacionais.js— feriados nacionais (Lei nº 662/1949 com redação da Lei nº 10.607/2002, Lei nº 6.802/1980, Lei nº 14.759/2023), Sexta-feira da Paixão, pontos facultativos federais (que não são feriados) e datas comemorativas.EXTRAS_POR_ANOguarda as emendas específicas de cada portaria (2026: Portaria MGI nº 11.460/2025).feriados-estaduais.js— 24 UFs verificadas; MG, MT e PR não têm feriado estadual próprio além dos nacionais.feriados-municipais.js— somente capitais com lei encontrada (21). Não é uma base municipal completa, e a interface diz isso.
Datas móveis são calculadas a partir da Páscoa (algoritmo de Meeus/Jones/
Butcher): Carnaval (−48/−47 dias), Quarta-feira de Cinzas (−46), Sexta-feira
Santa (−2) e Corpus Christi (+60). Regras suportadas: fixa, pascoa,
nesimo (ex.: 2º domingo de maio) e domingoSeguinte (ex.: data magna de SC).
Como atualizar: a cada nova portaria anual do MGI (dezembro/janeiro),
adicione o ano em EXTRAS_POR_ANO (datas extras, transferências, horários dos
pontos parciais) e ajuste fonte. Para leis estaduais/municipais novas,
inclua uma entrada com base_legal, fonte, desde/ate e rode npm test.
Cada item exibe sua base legal e fonte na página.
Outros países (calendário): api/feriados.js consulta a
Nager.Date por país e ano, só para os países das
cidades do relógio mundial (scripts/dados/paises.js, fonte única dos
códigos ISO). A CDN guarda cada resposta por 1 dia; o navegador guarda por 30
dias (scripts/nucleo/feriados-paises.js) e usa a cópia salva se a consulta
falhar. Países que a fonte não cobre (hoje IL, AE, IR, IN, NP) não aparecem
na lista. As datas chegam como AAAA-MM-DD e viram dias civis sem passar
por Date, então nenhum fuso desloca um feriado.
api/clima.js usa a Open-Meteo (CC BY 4.0, uso não
comercial gratuito) para a previsão (atual, 48 h exibidas, 16 dias — o maior
horizonte da fonte; a página marca os dias além de 7 como tendência) e para a
busca de cidades, e o Nominatim/OpenStreetMap só para dar nome à posição do
GPS. Coordenadas são arredondadas a 2 casas (~1 km). Cache na CDN: previsão
10 min, busca 1 dia, nomes 30 dias. Na primeira visita, sem lugar salvo, a
localização aproximada vem dos cabeçalhos x-vercel-ip-* (só em produção;
resposta private, no-store). O navegador guarda o lugar e a última previsão;
se a consulta falhar, mostra a cópia salva avisando que pode estar
desatualizada. Se o site passar a ter uso comercial, a Open-Meteo exige o
plano pago.
manifest.webmanifest+ ícones emassets/icones/permitem instalar o site.sw.jspré-carrega todas as páginas e arquivos estáticos. Páginas: rede primeiro (com limite de 3,5 s), depois cache, depois/offline. Arquivos estáticos: cache primeiro com atualização em segundo plano./api/*nunca passa pelo service worker nem entra no cache.- A versão do cache é um hash do conteúdo, gerado por
npm run gerar. Quando há versão nova, a página mostra "Nova versão disponível — Atualizar"; se ninguém clicar, ela assume quando todas as abas forem fechadas. Caches antigos são apagados na ativação. - Offline, todas as ferramentas que não dependem de rede funcionam; o relógio informa que não está sincronizado.
vercel.json:
cleanUrls: true—/cronometro.htmlresponde em/cronometro.- Redirecionamentos permanentes das URLs antigas (
/paginas/*.html) e do endereço antigobrasil-time.vercel.appparaglobal-time.lzdev.com.br(exceto/api/*e/sw.js, para que PWAs instaladas no endereço antigo continuem atualizando). X-Robots-Tag: noindexem qualquer host*.vercel.app(prévias): só o domínio oficial é indexado.- Segurança em todas as rotas: CSP restritiva (
script-src 'self', sem scripts ou estilos inline),X-Content-Type-Options,X-Frame-Options,Referrer-Policy,Permissions-Policy, HSTS. /api/hora,/api/conta,/api/eventos:no-storetambém no CDN;/api/feriadose/api/climadefinem o próprio cache (CDN, nunca o navegador)./sw.js:no-cache./assets/fontes/*: cache imutável de 1 ano. HTML, CSS e JS usam o padrão da Vercel (revalidação a cada acesso), para atualizações imediatas.
Validação: npm run test:navegador (todas as rotas) e os comandos curl da
seção de publicação.
| Recurso | Usado em | Observação |
|---|---|---|
| Áudio | Temporizador, Pomodoro, Despertador | Navegadores só liberam som após um clique na página |
| Notificações | Temporizador, Pomodoro, Despertador | Pedidas apenas ao clicar em "Ativar notificações" |
| Wake Lock (tela ligada) | Temporizador, tela cheia da página inicial, Despertador | Opcional; não disponível em todos os navegadores |
| Tela cheia | Página inicial | Só por clique ou tecla F; sem a API (iPhone), o relógio ocupa a janela |
| Geolocalização | Nascer e pôr do sol, Clima | Apenas ao clicar em "Usar minha localização"; recusar não impede buscar a cidade |
Segundo plano: temporizadores, Pomodoro e alarmes só funcionam com a página aberta (pode estar em outra aba). Com o navegador fechado ou o aparelho suspenso, nada toca — isso é uma limitação da plataforma web, avisada nas próprias páginas. Ao reabrir, o temporizador informa quando terminou e o despertador informa alarmes perdidos.
Global-Time/
├── *.html # uma página por ferramenta, em pt-BR (URLs limpas na Vercel)
├── en/, es/, pt-pt/, … # versões traduzidas, geradas por npm run gerar
├── 404.html, offline.html
├── api/hora.js # Vercel Function: timestamp do servidor (no-store)
├── api/conta.js # contas: cadastro, login, sessão, exclusão
├── api/eventos.js # eventos pessoais da conta
├── api/feriados.js # feriados de outros países (Nager.Date)
├── api/clima.js # clima, previsão e busca de cidades (Open-Meteo, Nominatim)
├── api/_lib/ # HTTP, Upstash/memória, senhas e sessões (não é rota)
├── estilos/app.css # design system + estilos de todas as páginas
├── scripts/
│ ├── tema-inicial.js # aplica o tema antes da pintura (script clássico)
│ ├── nucleo/ # módulos compartilhados (sincronização, fusos, medidores,
│ │ # alertas, atalhos, feriados, UI, layout comum)
│ ├── paginas/ # lógica de cada página
│ ├── i18n/ # idiomas, t(), dicionários (textos/)
│ └── dados/ # feriados, UFs e cidades
├── assets/
│ ├── marca/ # logo, símbolo, imagem Open Graph
│ ├── icones/ # ícones PNG do PWA
│ ├── icones.svg # sprite de ícones da interface
│ └── fontes/ # Inter e Plus Jakarta Sans (woff2, OFL)
├── sw.js, manifest.webmanifest, favicon.ico, robots.txt, sitemap.xml
├── vercel.json, .vercelignore, package.json
├── dev/ # servidor local, gerador de layout, testes de navegador/PWA
└── testes/ # testes unitários (node:test)
Enzinxz, Luizeh — com a identidade visual e a reestruturação da nova versão alinhadas ao padrão da LZdev.
Licença MIT.