github.com/richardwilkes/toolbox@v1.121.0/formats/xlsx/datetime.go (about)

     1  // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the Mozilla Public
     4  // License, version 2.0. If a copy of the MPL was not distributed with
     5  // this file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  //
     7  // This Source Code Form is "Incompatible With Secondary Licenses", as
     8  // defined by the Mozilla Public License, version 2.0.
     9  
    10  package xlsx
    11  
    12  import (
    13  	"math"
    14  	"time"
    15  )
    16  
    17  // TimeFromExcelTime convertx an Excel time representation to a time.Time. Code for date/time conversion adapted from
    18  // github.com/tealeg/xlsx.
    19  func timeFromExcelTime(excelTime float64) time.Time {
    20  	var date time.Time
    21  	intPart := int64(excelTime)
    22  	// Excel uses Julian dates prior to March 1st 1900, and
    23  	// Gregorian thereafter.
    24  	if intPart <= 61 {
    25  		return julianDateToGregorianTime(2400000.5, excelTime+15018.0)
    26  	}
    27  	floatPart := excelTime - float64(intPart)
    28  	var dayNanoSeconds float64 = 24 * 60 * 60 * 1000 * 1000 * 1000
    29  	date = time.Date(1899, 12, 30, 0, 0, 0, 0, time.UTC)
    30  	durationDays := time.Duration(intPart) * time.Hour * 24
    31  	durationPart := time.Duration(dayNanoSeconds * floatPart)
    32  	return date.Add(durationDays).Add(durationPart)
    33  }
    34  
    35  func julianDateToGregorianTime(part1, part2 float64) time.Time {
    36  	part1I, part1F := math.Modf(part1)
    37  	part2I, part2F := math.Modf(part2)
    38  	julianDays := part1I + part2I
    39  	julianFraction := part1F + part2F
    40  	julianDays, julianFraction = shiftJulianToNoon(julianDays, julianFraction)
    41  	day, month, year := fliegelAndVanFlandernAlgorithm(int(julianDays))
    42  	hours, minutes, seconds, nanoseconds := fractionOfADay(julianFraction)
    43  	return time.Date(year, time.Month(month), day, hours, minutes, seconds, nanoseconds, time.UTC)
    44  }
    45  
    46  func shiftJulianToNoon(julianDays, julianFraction float64) (julianDaysResult, julianFractionResult float64) {
    47  	switch {
    48  	case -0.5 < julianFraction && julianFraction < 0.5:
    49  		julianFraction += 0.5
    50  	case julianFraction >= 0.5:
    51  		julianDays++
    52  		julianFraction -= 0.5
    53  	case julianFraction <= -0.5:
    54  		julianDays--
    55  		julianFraction += 1.5
    56  	}
    57  	return julianDays, julianFraction
    58  }
    59  
    60  func fliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) {
    61  	l := jd + 68569
    62  	n := (4 * l) / 146097
    63  	l -= (146097*n + 3) / 4
    64  	i := (4000 * (l + 1)) / 1461001
    65  	l = l - (1461*i)/4 + 31
    66  	j := (80 * l) / 2447
    67  	d := l - (2447*j)/80
    68  	l = j / 11
    69  	m := j + 2 - (12 * l)
    70  	y := 100*(n-49) + i + l
    71  	return d, m, y
    72  }
    73  
    74  func fractionOfADay(fraction float64) (hours, minutes, seconds, nanoseconds int) {
    75  	const (
    76  		c1us = 1e3
    77  		c1s  = 1e9
    78  	)
    79  	frac := int64(24*60*60*c1s*fraction + c1us/2)
    80  	nanoseconds = int((frac%c1s)/c1us) * c1us
    81  	frac /= c1s
    82  	seconds = int(frac % 60)
    83  	frac /= 60
    84  	minutes = int(frac % 60)
    85  	hours = int(frac / 60)
    86  	return
    87  }