Wednesday, September 19, 2012

计算两个日期间隔,用X年X月X日表示

自己用golang写了一个短代码用来计算两个日期间隔,间隔用X年X月X日表示出来,比如2010-07-19到2012-09-18的间隔就是2年1月30天。写这个程序是用来具体算小孩的年龄,闰年也有考虑在内,比如 2010-07-19到2012-03-18是1年7个月28天,因为2012年2月有29天。

package main

import (
    "flag"
    "fmt"
    "os"
    "strconv"
    "strings"
    "time"
)

type DateDiff struct {
    years, months, days int
}

var DAYS = map[time.Month]int{
    time.January:   31,
    time.February:  28,
    time.March:     31,
    time.April:     30,
    time.May:       31,
    time.June:      30,
    time.July:      31,
    time.August:    31,
    time.September: 30,
    time.October:   31,
    time.November:  30,
    time.December:  31,
}

func LeapYear(year int) bool {
    ret := false
    if year%4 == 0 {
        if year%100 != 0 {
            ret = true
        } else if year%400 == 0 {
            ret = true
        }
    }
    return ret
}

// passed in strings hould be in either of the following two format:
//    yyyy-mm-dd
//    yyyy/mm/dd
func ParseDate(s string) time.Time {
    var it []string
    if strings.Index(s, "-") > 0 {
        it = strings.Split(s, "-")
    } else if strings.Index(s, "/") > 0 {
        it = strings.Split(s, "/")
    } else {
        fmt.Printf("Error: wrong date format\n")
        flag.Usage()
        os.Exit(-1)
    }
    y, err := strconv.ParseInt(it[0], 10, 64)
    if err != nil {
        panic(err)
    }
    m, err := strconv.ParseInt(it[1], 10, 64)
    if err != nil {
        panic(err)
    }
    d, err := strconv.ParseInt(it[2], 10, 64)
    if err != nil {
        panic(err)
    }
    return time.Date(int(y), time.Month(int(m)), int(d), 0, 0, 0, 0, time.UTC)
}

func main() {
    var ds, de string
    flag.StringVar(&ds, "start", "", "[*]Start Date in format of yyyy/mm/dd or yyyy-mm-dd")
    flag.StringVar(&de, "end", "today", "End Date in format of yyyy/mm/dd or yyyy-mm-dd")
    flag.Parse()
    if ds == "" {
        fmt.Printf("Error: No start date is specified\n")
        flag.Usage()
        os.Exit(-1)
    }

    var start, end time.Time
    start = ParseDate(ds)
    if de == "today" {
        end = time.Now()
    } else {
        end = ParseDate(de)
    }

    var diff DateDiff
    var borrowed bool = false
    var daysBorrowed int = 0
    // Days difference
    if end.Day() >= start.Day() {
        diff.days = end.Day() - start.Day()
    } else {
        daysBorrowed = DAYS[end.Month()-1]
        if LeapYear(end.Year()) && end.Month() == time.March {
            daysBorrowed++ // February in leap year is 29 days
        }
        diff.days = end.Day() + daysBorrowed - start.Day()
        borrowed = true
    }

    // Month Difference
    endMonth := end.Month()
    if borrowed == true {
        if endMonth == time.January {
            endMonth = time.December
        } else {
            endMonth--
        }
        borrowed = false
    }
    if endMonth >= start.Month() {
        diff.months = int(endMonth) - int(start.Month())
    } else {
        diff.months = int(endMonth) + 12 - int(start.Month())
        borrowed = true
    }

    // Year difference
    if borrowed {
        diff.years = end.Year() - 1 - start.Year()
    } else {
        diff.years = end.Year() - start.Year()
    }

    // Output
    if diff.years < 0 || diff.months < 0 || diff.days < 0 {
        fmt.Printf("Error: end date should after start date\n")
    } else {
        fmt.Printf("Difference of %d Years %d Months %d Days\n", diff.years, diff.months, diff.days)
    }
}