Formát data JavaScript

Aktualizace: Níže uvedená dokumentace byla aktualizována pro nový formát data 1.2. Získejte to hned!

Ačkoli JavaScript poskytuje spoustu metod pro získání a nastavení částí objektu data, chybí mu jednoduchý způsob formátování data a času podle uživatelem zadané masky. Existuje několik skriptů, které tuto funkci poskytují, ale nikdy jsem neviděl žádný, který by mi fungoval dobře... Většina z nich je zbytečně objemná nebo pomalá, spojuje nesouvisející funkce, používá komplikované syntaxe masek, které víceméně vyžadují, abyste si přečetli dokumentaci pokaždé, když je chcete použít, nebo nepočítejte se speciálními případy, jako jsou escapování znaků masky ve vygenerovaném řetězci.

Když jsem vybíral, které speciální znaky masky použít pro můj JavaScript formátovač data, podíval jsem se na funkci data PHP a diskrétní funkce dateFormat a timeFormat ColdFusion. PHP používá šílenou směs písmen (alespoň pro mě, protože nejsem PHP programátor) k reprezentaci různých datových entit, a i když si pravděpodobně nikdy nezapamatuji celý seznam, nabízí výhody, že můžete použít obojí. formátování data a času pomocí jedné funkce a žádný ze speciálních znaků se nepřekrývá (na rozdíl od ColdFusion, kde m a mm znamenají různé věci v závislosti na tom, zda máte co do činění s daty nebo časy). Na druhou stranu ColdFusion používá pro masky velmi snadno zapamatovatelné speciální znaky.

S mým formátovačem dat jsem se snažil vzít ty nejlepší vlastnosti z obou a přidat trochu vlastního cukru. Skončilo to hodně jako implementace ColdFusion, protože jsem primárně používal syntaxi masky CF.

Než se dostaneme k dalším podrobnostem, zde je několik příkladů, jak lze tento skript použít:

var now = new Date();

now.format("m/dd/yy");
// Returns, e.g., 6/09/07

// Can also be used as a standalone function
dateFormat(now, "dddd, mmmm dS, yyyy, h:MM:ss TT");
// Saturday, June 9th, 2007, 5:46:21 PM

// You can use one of several named masks
now.format("isoDateTime");
// 2007-06-09T17:46:21

// ...Or add your own
dateFormat.masks.hammerTime = 'HH:MM! "Can\'t touch this!"';
now.format("hammerTime");
// 17:46! Can't touch this!

// When using the standalone dateFormat function,
// you can also provide the date as a string
dateFormat("Jun 9 2007", "fullDate");
// Saturday, June 9, 2007

// Note that if you don't include the mask argument,
// dateFormat.masks.default is used
now.format();
// Sat Jun 09 2007 17:46:21

// And if you don't include the date argument,
// the current date and time is used
dateFormat();
// Sat Jun 09 2007 17:46:22

// You can also skip the date argument (as long as your mask doesn't
// contain any numbers), in which case the current date/time is used
dateFormat("longTime");
// 5:46:22 PM EST

// And finally, you can convert local time to UTC time. Either pass in
// true as an additional argument (no argument skipping allowed in this case):
dateFormat(now, "longTime", true);
now.format("longTime", true);
// Both lines return, e.g., 10:46:21 PM UTC

// ...Or add the prefix "UTC:" to your mask.
now.format("UTC:h:MM:ss TT Z");
// 10:46:21 PM UTC

Následují podporované speciální znaky. Jakékoli rozdíly ve významu od dateFormat ColdFusion a timeFormat funkce jsou zaznamenány.

Maska Popis
d Den v měsíci jako číslice; žádná úvodní nula pro jednociferné dny.
dd Den v měsíci jako číslice; úvodní nula pro jednociferné dny.
ddd Den v týdnu jako třípísmenná zkratka.
dddd Úplný název dne v týdnu.
m Měsíc jako číslice; žádná úvodní nula pro jednociferné měsíce.
mm Měsíc jako číslice; úvodní nula pro jednociferné měsíce.
mmm Měsíc jako třípísmenná zkratka.
mmmm Měsíc jako celý název.
yy Rok jako poslední dvě číslice; vedoucí nula pro roky menší než 10.
yyyy Rok reprezentovaný čtyřmi číslicemi.
h Hodiny; žádná úvodní nula pro jednociferné hodiny (12hodinový formát).
hh Hodiny; úvodní nula pro jednociferné hodiny (12hodinový formát).
H Hodiny; žádná úvodní nula pro jednociferné hodiny (24hodinový formát).
HH Hodiny; úvodní nula pro jednociferné hodiny (24hodinový formát).
M minuty; žádná úvodní nula pro jednociferné minuty.
Velké M na rozdíl od CF timeFormat 's m se vyhnout konfliktu s měsíci.
MM minuty; úvodní nula pro jednociferné minuty.
Velké MM na rozdíl od CF timeFormat 's mm, aby nedošlo ke konfliktu s měsíci.
s Sekundy; žádná úvodní nula pro jednociferné sekundy.
ss Sekundy; úvodní nula pro jednociferné sekundy.
l nebo L Milisekundy. l dává 3 číslice. L dává 2 číslice.
t Malý, jednoznakový řetězec časové značky:a nebo p .
Žádný ekvivalent v CF.
tt Řetězec časové značky o dvou znacích malými písmeny:am nebo odpoledne .
Žádný ekvivalent v CF.
T Řetězec časové značky tvořený velkými písmeny:A nebo P .
Velké T na rozdíl od CF t umožňuje uživatelsky specifikovaná velká písmena.
TT Řetězec časové značky o dvou znacích velkými písmeny:AM nebo PM .
Velká písmena TT na rozdíl od TT CF umožňují uživatelsky specifikovaná velká a malá písmena.
Z Zkratka časového pásma USA, např. EST nebo MDT . U časových pásem mimo USA nebo v prohlížeči Opera se posun GMT/UTC vrátí, např. GMT-0500
Žádný ekvivalent v CF.
o Posun časového pásma GMT/UTC, např. -0500 nebo +0230 .
Žádný ekvivalent v CF.
S Řadová přípona data (st , nd , rd nebo th ). Funguje dobře s d .
Žádný ekvivalent v CF.
'…' nebo "…" Doslovná sekvence znaků. Okolní uvozovky jsou odstraněny.
Žádný ekvivalent v CF.
UTC: Musí to být první čtyři znaky masky. Před aplikací masky převede datum z místního času na čas UTC/GMT/Zulu. Předpona "UTC:" je odstraněna.
Žádný ekvivalent v CF.

A zde jsou pojmenované masky poskytované ve výchozím nastavení (můžete je snadno změnit nebo přidat vlastní):

Jméno Maska Příklad
výchozí ddd mmm dd yyyy HH:MM:ss So 9. června 2007 17:46:21
shortDate m/d/rr 6/9/07
střední Datum d mmm, yyyy 9. června 2007
longDate d mmmm, yyyy 9. června 2007
fullDate dddd, mmmm d, yyyy Sobota 9. června 2007
krátký čas h:MM TT 17:46
střední čas h:MM:ss TT 17:46:21
dlouhodobě h:MM:ss TT Z 17:46:21 EST
isoDate yyyy-mm-dd 2007-06-09
isoTime HH:MM:ss 17:46:21
isoDateTime yyyy-mm-dd'T'HH:MM:ss 2007-06-09T17:46:21
isoUtcDateTime UTC:yyyy-mm-dd'T'HH:MM:ss'Z' 2007-06-09T22:46:21Z

Pár problémů:

  • V nepravděpodobném případě, že by význam vaší masky byl nejednoznačný (např. m následuje mm , bez oddělovacích znaků), vložte mezi své metasekvence pár prázdných uvozovek. Uvozovky budou automaticky odstraněny.
  • Pokud potřebujete do masky zahrnout doslovné uvozovky, platí následující pravidla:
    • Nespárované nabídky nevyžadují zvláštní zacházení.
    • Chcete-li do masek, které obsahují jakékoli jiné uvozovky stejného typu, zahrnout doslovné uvozovky, musíte je uzavřít alternativním typem uvozovek (tj. dvojité uvozovky pro jednoduché uvozovky a naopak). Např. date.format('h "o\'clock, y\'all!"') vrátí "6 hodin, všichni". Možná to může být trochu chlupaté, ale pochybuji, že se s tím lidé budou setkávat tak často. Předchozí příklad lze také zapsat jako date.format("h") + "o'clock, y'all!" .

Zde je kód:

/*
 * Date Format 1.2.3
 * (c) 2007-2009 Steven Levithan <stevenlevithan.com>
 * MIT license
 *
 * Includes enhancements by Scott Trenda <scott.trenda.net>
 * and Kris Kowal <cixar.com/~kris.kowal/>
 *
 * Accepts a date, a mask, or a date and a mask.
 * Returns a formatted version of the given date.
 * The date defaults to the current date/time.
 * The mask defaults to dateFormat.masks.default.
 */

var dateFormat = function () {
	var	token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
		timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
		timezoneClip = /[^-+\dA-Z]/g,
		pad = function (val, len) {
			val = String(val);
			len = len || 2;
			while (val.length < len) val = "0" + val;
			return val;
		};

	// Regexes and supporting functions are cached through closure
	return function (date, mask, utc) {
		var dF = dateFormat;

		// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
		if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
			mask = date;
			date = undefined;
		}

		// Passing date through Date applies Date.parse, if necessary
		date = date ? new Date(date) : new Date;
		if (isNaN(date)) throw SyntaxError("invalid date");

		mask = String(dF.masks[mask] || mask || dF.masks["default"]);

		// Allow setting the utc argument via the mask
		if (mask.slice(0, 4) == "UTC:") {
			mask = mask.slice(4);
			utc = true;
		}

		var	_ = utc ? "getUTC" : "get",
			d = date[_ + "Date"](),
			D = date[_ + "Day"](),
			m = date[_ + "Month"](),
			y = date[_ + "FullYear"](),
			H = date[_ + "Hours"](),
			M = date[_ + "Minutes"](),
			s = date[_ + "Seconds"](),
			L = date[_ + "Milliseconds"](),
			o = utc ? 0 : date.getTimezoneOffset(),
			flags = {
				d:    d,
				dd:   pad(d),
				ddd:  dF.i18n.dayNames[D],
				dddd: dF.i18n.dayNames[D + 7],
				m:    m + 1,
				mm:   pad(m + 1),
				mmm:  dF.i18n.monthNames[m],
				mmmm: dF.i18n.monthNames[m + 12],
				yy:   String(y).slice(2),
				yyyy: y,
				h:    H % 12 || 12,
				hh:   pad(H % 12 || 12),
				H:    H,
				HH:   pad(H),
				M:    M,
				MM:   pad(M),
				s:    s,
				ss:   pad(s),
				l:    pad(L, 3),
				L:    pad(L > 99 ? Math.round(L / 10) : L),
				t:    H < 12 ? "a"  : "p",
				tt:   H < 12 ? "am" : "pm",
				T:    H < 12 ? "A"  : "P",
				TT:   H < 12 ? "AM" : "PM",
				Z:    utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
				o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
				S:    ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
			};

		return mask.replace(token, function ($0) {
			return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
		});
	};
}();

// Some common format strings
dateFormat.masks = {
	"default":      "ddd mmm dd yyyy HH:MM:ss",
	shortDate:      "m/d/yy",
	mediumDate:     "mmm d, yyyy",
	longDate:       "mmmm d, yyyy",
	fullDate:       "dddd, mmmm d, yyyy",
	shortTime:      "h:MM TT",
	mediumTime:     "h:MM:ss TT",
	longTime:       "h:MM:ss TT Z",
	isoDate:        "yyyy-mm-dd",
	isoTime:        "HH:MM:ss",
	isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
	isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
};

// Internationalization strings
dateFormat.i18n = {
	dayNames: [
		"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
		"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
	],
	monthNames: [
		"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
		"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
	]
};

// For convenience...
Date.prototype.format = function (mask, utc) {
	return dateFormat(this, mask, utc);
};

Stáhněte si jej zde (1,2 kB při minifikaci a gzipování).

Upozorňujeme, že názvy dne a měsíce lze změnit (pro internacionalizaci nebo jiné účely) aktualizací dateFormat.i18n objekt.

Pokud máte nějaké návrhy nebo najdete nějaké problémy, dejte mi vědět.

Chcete se dozvědět o afantazii a hyperfantazii, kultu Shen Yun nebo odhalování vůdkyně kultu Karen Zerby? Podívejte se na můj nový blog na Life After Tech.