Movatterモバイル変換


[0]ホーム

URL:


Skip to main content

Dates and Times

File Format Support (click to show)

Dates are a core concept in nearly every spreadsheet application in existence.Some legacy spreadsheet apps only supported dates. Others supported times as adistinct concept from dates.

Some file formats store dates in a textual format, while others store dates withnumbers representing a difference from an epoch.

Many spreadsheet apps use special number formats to signal that values are datesor times. Quattro Pro for DOS had a distinct set of Date number formats andTime number formats, but did not have a mixed Date + Time format. OpenOfficeuses ISO 8601 duration strings for pure time data.

Lotus 1-2-3 used a"1900" date system, whileNumbers exclusively supports 1904 under the hood. Excel file formats typicallyinclude options for specifying the date system. OpenOffice can support arbitrarystarting dates.

FormatsDateTimeD+TDate StorageDate System
NUMBERSNumber1904 Only
XLSX / XLSMNumber1900 + 1904
XLSX (Strict ISO)Relative Date1900 + 1904
XLSBNumber1900 + 1904
XLMLRelative Date1900 + 1904
XLS (BIFF5/8)Number1900 + 1904
XLS (BIFF2/3/4)Number1900 + 1904
XLR (Works)Number1900 + 1904
ET (WPS 电子表格)Number1900 + 1904
ODS / FODS / UOSISO Duration or DateArbitrary
HTMLPlaintextCalendar
CSV / TSV / TextPlaintextCalendar
DBF**Number or PlaintextCalendar
DIFPlaintextCalendar
WK1Number1900
WKS (Works)Number1900
WQ1Number1900
QPW*Number1900

X (✕) marks features that are not supported by the file formats. For example,the WK1 file format had date-only formats and time-only formats but no mixeddate-time formats.

Newer DBF levels support a specialT field type that represents date + time.

The QPW file format supports mixed date + time formats in custom number formats.

Lotus 1-2-3, Excel, and other spreadsheet software do not have a true conceptof date or time. Instead, dates and times are stored as offsets from an epoch.The magic behind date interpretations is hidden in functions or number formats.

SheetJS attempts to create a friendly JS date experience while also exposingoptions to use the traditional date codes.

Date and time handling was overhauled in version0.20.0. It is stronglyrecommended toupgrade.

The following example exports the current time to XLSX spreadsheet. The timeshown on this page will be the time displayed in Excel.

  • React (Live Demo)
  • JavaScript (Explanation)
Result
Loading...
Live Editor
functionSheetJSNow(){const[date, setDate]=React.useState(newDate());const xport=React.useCallback(()=>{/* generate array of arrays */const aoa=[[date]];/* to avoid confusion, set milliseconds to 0 */    aoa[0][0].setMilliseconds(0);/* generate workbook */const ws=XLSX.utils.aoa_to_sheet(aoa,{dense:true});/* set cell A1 number format */    ws["!data"][0][0].z="yyyy-mm-dd hh:mm:ss"    ws["!cols"]=[{wch:20}];/* generate workbook and export */const wb=XLSX.utils.book_new(ws,"Sheet1");XLSX.writeFile(wb,"SheetJSNow.xlsx");},[]);return(<><p><b>Local Time:</b>{date.toString()}<buttononClick={()=>setDate(newDate())}>Refresh</button></p><buttononClick={xport}>Export XLSX</button></>);}

How Spreadsheets Understand Time

Excel stores dates as numbers. When displaying dates, the format code shouldinclude special date and time tokens likeyyyy for long year.EDATE andother date functions operate on and return date numbers.

For date formats likeyyyy-mm-dd, the integer part represents the number ofdays from a starting epoch. For example, the date19-Feb-17 is stored as thenumber42785 with a number format ofd-mmm-yy.

The fractional part of the date code serves as the time marker. Excel assumeseach day has exactly 86400 seconds. For example, the date code0.25 has atime component corresponding to 6:00 AM.

For absolute time formats like[hh]:mm, the integer part represents a wholenumber of 24-hour (or 1440 minute) intervals. The value1.5 in the format[hh]:mm is interpreted as 36 hours 0 minutes.

Date and Time Number Formats

Assuming a cell has a formatted date, re-formatting as "General" will revealthe underlying value. Alternatively, theTEXT function can be used to returnthe date code.

The following table covers some common formats:

Common Date-Time Formats (click to show)
FragmentInterpretation
yyShort (2-digit) year
yyyyLong (4-digit) year
mShort (1-digit) month
mmLong (2-digit) month
mmmShort (3-letter) month name
mmmmFull month name
mmmmmFirst letter of month name
dShort (1-digit) day of month
ddLong (2-digit) day of month
dddShort (3-letter) day of week
ddddFull day of week
hShort (1-digit) hours
hhLong (2-digit) hours
mShort (1-digit) minutes
mmLong (2-digit) minutes
sShort (1-digit) seconds
ssLong (2-digit) seconds
A/PMeridiem ("A" or "P")
AM/PMMeridiem ("AM" or "PM")

m andmm are context-dependent. It is interpreted as "minutes" when theprevious or next date token represents a time (hours or seconds):

yyyy-mm-dd hh:mm:ss
^^ ^^
month minutes

mmm,mmmm, andmmmmm always represent months.

1904 and 1900 Date Systems

The interpretation of date codes requires a shared understanding of date code0, otherwise known as the "epoch". Excel supports two epochs:

  • The default epoch is "January 0 1900". The0 value is 00:00 on December 31of the year 1899, but it is formatted as January 0 1900.

  • Enabling "1904 Date System" sets the default epoch to "January 1 1904". The0 value is 00:00 on January 1 of the year 1904.

The workbook's epoch can be determined by examining the workbook'swb.Workbook.WBProps.date1904 property:

if(!(wb?.Workbook?.WBProps?.date1904)){
/* uses 1904 date system */
}else{
/* uses 1900 date system */
}
Why does the 1904 date system exist? (click to show)

1900 was not a leap year. For the Gregorian calendar, the general rules are:

  • every multiple of 400 is a leap year
  • every multiple of 100 that is not a multiple of 400 is not a leap year
  • every multiple of 4 that is not a multiple of 100 is a leap year
  • all other years are not leap years.

Lotus 1-2-3 erroneously treated 1900 as a leap year. This can be verified withthe@date function:

@date(0,2,28) -> 59    // Lotus accepts 2/28/1900
@date(0,2,29) -> 60 // <--2/29/1900 was not a real date
@date(0,2,30) -> ERR // Lotus rejects 2/30/1900

Excel extends the tradition in the default date system. The 1904 date systemstarts the count in 1904, skipping the bad date.

Relative Epochs

The epoch is based on the system timezone. The epoch in New York is midnightin Eastern time, while the epoch in Seattle is midnight in Pacific time.

This design has the advantage of uniform time display: "12 PM" is 12 PMirrespective of the timezone of the viewer. However, this design precludes anyinternational coordination (there is no way to create a value that representsan absolute time) and makes JavaScript processing somewhat ambiguous (sinceJavaScript Date objects are timezone-aware)

This is a deficiency of the spreadsheet software. Excel has no native conceptof universal time.

How Files Store Dates and Times

Technical Details (click to show)

XLS, XLSB, and most binary formats store the raw date codes. Special numberformats are used to indicate that the values are intended to be dates/times.

CSV and other text formats typically store actual formatted date values. Theyare interpreted as dates and times in the user timezone.

XLSX actually supports both! Typically dates are stored asn numeric cells,but the format supports a special typed where the data is an ISO 8601 datestring. This is not used in the default Excel XLSX export and third-partysupport is poor.

ODS does support absolute time values but drops the actual timezone indicatorwhen parsing. In that sense, LibreOffice follows the same behavior as Excel.

Numbers uses a calendar date system, but records pure time values as if they areabsolute times in 1904 January 01. It is spiritually equivalent to the 1904 modein Excel and other spreadsheet applications.

How JavaScript Engines Understand Time

JavaScript provides aDate object which represents anabsolute time. Underthe hood,Date uses the "UNIX" epoch of 1970 January 01 midnight in UTC. Thismeans the actual zero date is different in different timezones!

LocationIANA Timezonenew Date(0) in local time
HonoluluPacific/Honolulu1969-12-31 02:00 PM
Los AngelesAmerica/Los_Angeles1969-12-31 04:00 PM
New YorkAmerica/New_York1969-12-31 07:00 PM
Sao PauloAmerica/Sao_Paulo1969-12-31 09:00 PM
LondonEurope/London1970-01-01 01:00 AM
CairoAfrica/Cairo1970-01-01 02:00 AM
DjiboutiAfrica/Djibouti1970-01-01 03:00 AM
ChennaiAsia/Kolkata1970-01-01 05:30 AM
ShanghaiAsia/Shanghai1970-01-01 08:00 AM
SeoulAsia/Seoul1970-01-01 09:00 AM
SydneyAustralia/Sydney1970-01-01 10:00 AM

In modern environments, the IANA Timezone and timezone offset can be discoveredthrough theIntl andDate objects:

Result
Loading...
Live Editor
functionLocalInfo(){const date=newDate();return(<><b>Local Time</b>:{date.toString()}<br/><b>Time offset (relative to UTC)</b>:{-date.getTimezoneOffset()/60} hours<br/><b>IANA Timezone</b>:{Intl.DateTimeFormat().resolvedOptions().timeZone}</>)}

The timezone information is provided by the JavaScript engine and local settings.There are outstanding Google Chrome and V8 bugs related to rounded offsets fortimezones under a lunar calendar. The last timezone to switch to the Gregoriancalendar wasAfrica/Monrovia (in 1972).

SheetJS utilities attempt to work around the browser bugs.

UTC and Local Time

TheDate object has a number of prototype methods for inspecting the object.Some methods interact with the true value, while others convert to the localtimezone. Some methods are listed in the table below:

FeatureLocal Time methodUTC method
YeargetFullYeargetUTCFullYear
Month (0-11)getMonthgetUTCMonth
Day of the monthgetDategetUTCDate
HoursgetHoursgetUTCHours
MinutesgetMinutesgetUTCMinutes
SecondsgetSecondsgetUTCSeconds
Entire datetoStringtoUTCString

It is typical for websites and other applications to present data in local time.To serve an international audience, backend servers typically use UTC time.

The following example shows the time when the page was loaded. The same absolutetime will appear to be different under local and UTC interpretations:

Result
Loading...
Live Editor
functionLocalUTC(){const d=newDate();/* display number with 2 digits, prepending `0` if necessary */constf=(n)=> n.toString().padStart(2,"0");/* HH:MM:SS using local interpretation */const local=`${f(d.getHours())}:${f(d.getMinutes())}:${f(d.getSeconds())}`;/* HH:MM:SS using UTC interpretation */const utc=`${f(d.getUTCHours())}:${f(d.getUTCMinutes())}:${f(d.getUTCSeconds())}`;return(<><b>Local Interpretation</b><br/><code>toString</code>:{d.toString()}<br/>    24-hour time:{local}<br/><br/><b>UTC Interpretation</b><br/><code>toUTCString</code>:{d.toUTCString()}<br/>    24-hour time:{utc}<br/></>)}

How SheetJS handles Dates and Times

SheetJS attempts to reconcile the spreadsheet and JavaScript date concepts.

The default behavior for all parsers is to generate number cells. SettingcellDates to true will force the parsers to store dates.

Result
Loading...
Live Editor
functionSheetJSCellDates(){var csv="Date,10/6/2048";// cell B1 will be { t: 'n', v: 54337 }var wb_sans_date=XLSX.read(csv,{type:"binary"});var ws_sans_date= wb_sans_date.Sheets.Sheet1;// cell B1 will be { t: 'd', v: <Date: 2048-10-06 00:00:00 UTC> }var wb_with_date=XLSX.read(csv,{type:"binary", cellDates:true});var ws_with_date= wb_with_date.Sheets.Sheet1;return(<><b>CSV:</b><pre>{csv}</pre><b>Cell B1:</b><br/><br/><table><tr><th>cellDates</th><th>type</th><th>value</th></tr><tr><td>(unspecified)</td><td><code>{ws_sans_date["B1"].t}</code></td><td><code>{ws_sans_date["B1"].v}</code></td></tr><tr><td>true</td><td><code>{ws_with_date["B1"].t}</code></td><td><code>{ws_with_date["B1"].v.toISOString()}</code> (Date object)</td></tr></table></>);}

When writing, date cells are automatically translated back to numeric cellswith an appropriate number format.

The value formatting logic understands date formats and converts when relevant.It always uses the UTC interpretation of Date objects.

Date Objects

The actual values stored in cells are intended to be correct when interpretedusing UTC date methods.

For example,DateTime.xlsx is a test file with the following data:

TypeValue
Date2048-10-06
Time15:00
DateTime2048-10-06 15:00:00

The raw data values are shown in the live demo. The UTC date string will showthe same value as Excel irrespective of the local timezone.

Result
Loading...
Live Editor
functionSheetJSDateTimeXlsxValues(){const[data, setData]=React.useState([[]]);React.useEffect(()=>{(async()=>{const ab=await(awaitfetch("/DateTime.xlsx")).arrayBuffer();const wb=XLSX.read(ab,{cellDates:true, dense:true});setData(wb.Sheets.Sheet1["!data"]);})();});return(<table><thead><th>Excel Date</th><th>UTC Date</th><th>Local Date</th></thead><tbody>{data.slice(1).map((row,R)=>(<trkey={R}><td>{row[1].w}</td><td>{row[1].v.toUTCString()}</td><td>{row[1].v.toString()}</td></tr>))}</tbody></table>);}

Utility Functions

Utility functions that deal with JS data accept acellDates argument whichdictates how dates should be handled.

Functions that create a worksheet will adjust date cells and use a numberformat likem/d/yy to mark dates:

// Cell A1 will be a numeric cell whose value is the date code
var ws=XLSX.utils.aoa_to_sheet([[newDate()]]);

// Cell A1 will be a date cell
var ws=XLSX.utils.aoa_to_sheet([[newDate()]],{cellDates:true});

Functions that create an array of JS objects with raw values will keep thenative representation:

// Cell A1 is numeric -> output is a number
var ws=XLSX.utils.aoa_to_sheet([[newDate()]]);
varA1=XLSX.utils.sheet_to_json(ws,{header:1})[0][0];

// Cell A1 is a date -> output is a date
var ws=XLSX.utils.aoa_to_sheet([[newDate()]],{cellDates:true});
varA1=XLSX.utils.sheet_to_json(ws,{header:1})[0][0];

UTC Option

Some API functions support theUTC option to control how dates are handled.

sheet_to_json

IfUTC is true, the dates will be correct when interpreted in UTC. By default,the dates will be correct when interpreted in local time.

TypicallyUTC is used for data from an API endpoint, as servers typically emitUTC dates and expect scripts to localize. The local interpretation is sensiblewhen users submit data, as they will be providing times in their local timezone.

aoa_to_sheet /sheet_add_aoa /json_to_sheet /sheet_add_json

IfUTC is true, the UTC interpretation of dates will be used.

TypicallyUTC is used for data from an API endpoint, as servers typically emitUTC dates and expect scripts to localize. The local interpretation is sensiblewhen date objects are generated in the browser.

table_to_book /table_to_sheet /sheet_add_dom

IfUTC is true, potential dates are interpreted as if they represent UTC times.By default, potential dates are interpreted in local time.

TypicallyUTC is used for data exported from Excel or other spreadsheet apps.If the table is programmatically generated in the frontend, the dates and timeswill be in the local timezone and the local interpretation is preferable.

Number Formats

By default, the number formats are not emitted. For Excel-based file formats,passing the optioncellNF: true adds thez field.

The helper functionXLSX.SSF.is_date parses formats and returnstrue if theformat represents a date or time:

XLSX.SSF.is_date("yyyy-mm-dd");// true

XLSX.SSF.is_date("0.00");// false
Live Demo (click to show)
Result
Loading...
Live Editor
functionSSFIsDate(){const[format, setFormat]=React.useState("yyyy-mm-dd");const cb=React.useCallback((evt)=>{setFormat(evt.target.value);});const is_date=XLSX.SSF.is_date(format);return(<><div>Format<b>|{format}|</b> is{is_date?"":"not"} a date/time</div><inputtype="text"onChange={cb}/></>);}

How JSON and APIs Understand Time

JSON does not have a native representation for JavaScript Date objects.

Starting from a Date object, theJSON.stringify method will encode the objectas a ISO 8601 date string. ApplyingJSON.parse to the result will return thestring rather than a proper Date object.

SheetJS utility functions will not try to interpret those strings as dates.Instead, the strings will be translated to text.

In the following example, the "ISO Text" data will be converted to a string cellwhile the "Date Obj" data will be converted to a spreadsheet date.

Result
Loading...
Live Editor
functionSheetJSONDates(){return(<buttononClick={()=>{const aoa=[["ISO Text","2048-10-06T00:00:00.000Z"],// B1 will be text["Date Obj",newDate("2048-10-06T00:00:00.000Z")]// B2 will be a date];const ws=XLSX.utils.aoa_to_sheet(aoa);const wb=XLSX.utils.book_new(ws,"Data");XLSX.writeFile(wb,"SheetJSONDates.xlsx");}}>Click to Export Sample Data</button>);}

Many API wrapper libraries return dates as strings instead of Date objects.

If the SheetJS operations generate string cells, review the documentation forthe wrapper library to ensure the other library is properly handling dates.

Fixing Arrays of Objects

Many APIs will return data as JSON objects. When particular fields are known tocontain date strings, they can be manually fixed.

For example, theExport Tutorial makesa final array of objects with birthdays stored as strings:

const rows=[
{name:"George Washington",birthday:"1732-02-22"},
{name:"John Adams",birthday:"1735-10-19"},
// ... one row per President
];

If this dataset is exported, thebirthday column will contain raw text values.A singleArray#map operation can create a fixed dataset:

const new_rows= rows.map(({birthday,...rest})=>({birthday:newDate(birthday),...rest}))

TheDate constructor interprets the dates in local time.

Excel and other spreadsheet software do not typically support dates before 1900.If there are dates before the threshold, it is strongly recommended to passstrings instead ofDate objects.

JavaScript string toDate conversion is "implementation-dependent" and maymisinterpret some date formats. When designing APIs, it is strongly recommendedto pass ISO 8601 strings when possible.


[8]ページ先頭

©2009-2025 Movatter.jp