From 3f902bd9763119a03aafe99638173b4b41643357 Mon Sep 17 00:00:00 2001 From: bulutmuf <211163136+bulutmuf@users.noreply.github.com> Date: Fri, 5 Jun 2026 14:09:03 +0300 Subject: [PATCH 1/2] Add AI credit billing usage reports Add organization and user billing methods for the AI credit usage endpoints. Cover request routing, query parameters, response decoding, and failure paths. --- github/billing.go | 121 +++++++++++++++++++++++++ github/billing_test.go | 196 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 317 insertions(+) diff --git a/github/billing.go b/github/billing.go index 9e9d812a0e0..a4ef1e3a8d9 100644 --- a/github/billing.go +++ b/github/billing.go @@ -116,6 +116,35 @@ type PremiumRequestUsageReportOptions struct { Product *string `url:"product,omitempty"` } +// AICreditUsageReportOptions specifies optional parameters +// for the enhanced billing platform AI credit usage report. +type AICreditUsageReportOptions struct { + // If specified, only return results for a single year. + // The value of year is an integer with four digits representing a year. For example, 2025. + // Default value is the current year. + Year int `url:"year,omitempty"` + + // If specified, only return results for a single month. + // The value of month is an integer between 1 and 12. Default value is the current month. + // If no year is specified the default year is used. + Month int `url:"month,omitempty"` + + // If specified, only return results for a single day. + // The value of day is an integer between 1 and 31. + // If no year or month is specified, the default year and month are used. + Day int `url:"day,omitempty"` + + // The user name to query usage for. The name is not case-sensitive. + // This parameter is only supported for organization-level reports. + User string `url:"user,omitempty"` + + // The model name to query usage for. The name is not case-sensitive. + Model string `url:"model,omitempty"` + + // The product name to query usage for. The name is not case-sensitive. + Product string `url:"product,omitempty"` +} + // UsageItem represents a single usage item in the enhanced billing platform report. type UsageItem struct { Date string `json:"date"` @@ -171,6 +200,40 @@ type PremiumRequestUsageReport struct { UsageItems []*PremiumRequestUsageItem `json:"usageItems"` } +// AICreditUsageItem represents a single usage line item in AI credit usage reports. +type AICreditUsageItem struct { + Product string `json:"product"` + SKU string `json:"sku"` + Model string `json:"model"` + UnitType string `json:"unitType"` + PricePerUnit float64 `json:"pricePerUnit"` + GrossQuantity float64 `json:"grossQuantity"` + GrossAmount float64 `json:"grossAmount"` + DiscountQuantity float64 `json:"discountQuantity"` + DiscountAmount float64 `json:"discountAmount"` + NetQuantity float64 `json:"netQuantity"` + NetAmount float64 `json:"netAmount"` +} + +// AICreditUsageTimePeriod represents a time period for AI credit usage reports. +type AICreditUsageTimePeriod struct { + Year int `json:"year"` + Month *int `json:"month,omitempty"` + Day *int `json:"day,omitempty"` +} + +// AICreditUsageReport represents the AI credit usage report response. +type AICreditUsageReport struct { + TimePeriod AICreditUsageTimePeriod `json:"timePeriod"` + // Organization is only set for organization-level reports. + Organization *string `json:"organization,omitempty"` + // User is only set for user-level reports. + User *string `json:"user,omitempty"` + Product *string `json:"product,omitempty"` + Model *string `json:"model,omitempty"` + UsageItems []*AICreditUsageItem `json:"usageItems"` +} + // GetOrganizationPackagesBilling returns the free and paid storage used for GitHub Packages in gigabytes for an Org. // // This endpoint appears to have disappeared from the official GitHub v3 API documentation website. @@ -392,3 +455,61 @@ func (s *BillingService) GetPremiumRequestUsageReport(ctx context.Context, user return premiumRequestUsageReport, resp, nil } + +// GetOrganizationAICreditUsageReport returns a report of the AI credit +// usage for an organization using the enhanced billing platform. +// +// Note: Only data from the past 24 months is accessible via this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/billing/usage?apiVersion=2022-11-28#get-billing-ai-credit-usage-report-for-an-organization +// +//meta:operation GET /organizations/{org}/settings/billing/ai_credit/usage +func (s *BillingService) GetOrganizationAICreditUsageReport(ctx context.Context, org string, opts *AICreditUsageReportOptions) (*AICreditUsageReport, *Response, error) { + u := fmt.Sprintf("organizations/%v/settings/billing/ai_credit/usage", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var aiCreditUsageReport *AICreditUsageReport + resp, err := s.client.Do(req, &aiCreditUsageReport) + if err != nil { + return nil, resp, err + } + + return aiCreditUsageReport, resp, nil +} + +// GetAICreditUsageReport returns a report of the AI credit +// usage for a user using the enhanced billing platform. +// +// Note: Only data from the past 24 months is accessible via this endpoint. +// +// GitHub API docs: https://docs.github.com/rest/billing/usage?apiVersion=2022-11-28#get-billing-ai-credit-usage-report-for-a-user +// +//meta:operation GET /users/{username}/settings/billing/ai_credit/usage +func (s *BillingService) GetAICreditUsageReport(ctx context.Context, user string, opts *AICreditUsageReportOptions) (*AICreditUsageReport, *Response, error) { + u := fmt.Sprintf("users/%v/settings/billing/ai_credit/usage", user) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest(ctx, "GET", u, nil) + if err != nil { + return nil, nil, err + } + + var aiCreditUsageReport *AICreditUsageReport + resp, err := s.client.Do(req, &aiCreditUsageReport) + if err != nil { + return nil, resp, err + } + + return aiCreditUsageReport, resp, nil +} diff --git a/github/billing_test.go b/github/billing_test.go index 1c1ba31fe20..bf2311b933b 100644 --- a/github/billing_test.go +++ b/github/billing_test.go @@ -661,6 +661,202 @@ func TestBillingService_GetPremiumRequestUsageReport_invalidUser(t *testing.T) { testURLParseError(t, err) } +func TestBillingService_GetOrganizationAICreditUsageReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/organizations/o/settings/billing/ai_credit/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "year": "2026", + "month": "6", + "user": "testuser", + "model": "Auto: Claude Sonnet 4.6", + }) + fmt.Fprint(w, `{ + "timePeriod": { + "year": 2026, + "month": 6 + }, + "organization": "GitHub", + "user": "testuser", + "product": "Copilot", + "model": "Auto: Claude Sonnet 4.6", + "usageItems": [ + { + "product": "Copilot", + "sku": "Copilot AI Credits", + "model": "Auto: Claude Sonnet 4.6", + "unitType": "ai-credits", + "pricePerUnit": 0.01, + "grossQuantity": 9498.6969165, + "grossAmount": 94.986969165, + "discountQuantity": 9564.710256, + "discountAmount": 95.64710256, + "netQuantity": 0.0, + "netAmount": 0.0 + } + ] + }`) + }) + ctx := t.Context() + opts := &AICreditUsageReportOptions{ + Year: 2026, + Month: 6, + User: "testuser", + Model: "Auto: Claude Sonnet 4.6", + } + report, _, err := client.Billing.GetOrganizationAICreditUsageReport(ctx, "o", opts) + if err != nil { + t.Errorf("Billing.GetOrganizationAICreditUsageReport returned error: %v", err) + } + want := &AICreditUsageReport{ + TimePeriod: AICreditUsageTimePeriod{ + Year: 2026, + Month: Ptr(6), + }, + Organization: Ptr("GitHub"), + User: Ptr("testuser"), + Product: Ptr("Copilot"), + Model: Ptr("Auto: Claude Sonnet 4.6"), + UsageItems: []*AICreditUsageItem{ + { + Product: "Copilot", + SKU: "Copilot AI Credits", + Model: "Auto: Claude Sonnet 4.6", + UnitType: "ai-credits", + PricePerUnit: 0.01, + GrossQuantity: 9498.6969165, + GrossAmount: 94.986969165, + DiscountQuantity: 9564.710256, + DiscountAmount: 95.64710256, + NetQuantity: 0.0, + NetAmount: 0.0, + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("Billing.GetOrganizationAICreditUsageReport returned %+v, want %+v", report, want) + } + + const methodName = "GetOrganizationAICreditUsageReport" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Billing.GetOrganizationAICreditUsageReport(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Billing.GetOrganizationAICreditUsageReport(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestBillingService_GetOrganizationAICreditUsageReport_invalidOrg(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Billing.GetOrganizationAICreditUsageReport(ctx, "%", nil) + testURLParseError(t, err) +} + +func TestBillingService_GetAICreditUsageReport(t *testing.T) { + t.Parallel() + client, mux, _ := setup(t) + mux.HandleFunc("/users/u/settings/billing/ai_credit/usage", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testFormValues(t, r, values{ + "year": "2026", + "day": "15", + "product": "Copilot AI Credits", + }) + fmt.Fprint(w, `{ + "timePeriod": { + "year": 2026, + "day": 15 + }, + "user": "monalisa", + "product": "Copilot AI Credits", + "usageItems": [ + { + "product": "Copilot AI Credits", + "sku": "AI Credit", + "model": "GPT-5", + "unitType": "ai-credits", + "pricePerUnit": 0.01, + "grossQuantity": 100, + "grossAmount": 1.0, + "discountQuantity": 0, + "discountAmount": 0.0, + "netQuantity": 100, + "netAmount": 1.0 + } + ] + }`) + }) + ctx := t.Context() + opts := &AICreditUsageReportOptions{ + Year: 2026, + Day: 15, + Product: "Copilot AI Credits", + } + report, _, err := client.Billing.GetAICreditUsageReport(ctx, "u", opts) + if err != nil { + t.Errorf("Billing.GetAICreditUsageReport returned error: %v", err) + } + want := &AICreditUsageReport{ + TimePeriod: AICreditUsageTimePeriod{ + Year: 2026, + Day: Ptr(15), + }, + User: Ptr("monalisa"), + Product: Ptr("Copilot AI Credits"), + UsageItems: []*AICreditUsageItem{ + { + Product: "Copilot AI Credits", + SKU: "AI Credit", + Model: "GPT-5", + UnitType: "ai-credits", + PricePerUnit: 0.01, + GrossQuantity: 100.0, + GrossAmount: 1.0, + DiscountQuantity: 0.0, + DiscountAmount: 0.0, + NetQuantity: 100.0, + NetAmount: 1.0, + }, + }, + } + if !cmp.Equal(report, want) { + t.Errorf("Billing.GetAICreditUsageReport returned %+v, want %+v", report, want) + } + + const methodName = "GetAICreditUsageReport" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Billing.GetAICreditUsageReport(ctx, "\n", opts) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Billing.GetAICreditUsageReport(ctx, "u", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestBillingService_GetAICreditUsageReport_invalidUser(t *testing.T) { + t.Parallel() + client, _, _ := setup(t) + + ctx := t.Context() + _, _, err := client.Billing.GetAICreditUsageReport(ctx, "%", nil) + testURLParseError(t, err) +} + func TestBillingService_PremiumRequestUsageItem_FloatQuantities(t *testing.T) { t.Parallel() client, mux, _ := setup(t) From f492be76f8456011f20a26e58a96f6993d3b2265 Mon Sep 17 00:00:00 2001 From: bulutmuf <211163136+bulutmuf@users.noreply.github.com> Date: Fri, 5 Jun 2026 14:09:11 +0300 Subject: [PATCH 2/2] Regenerate accessors Regenerate accessor helpers and tests for the new AI credit billing usage types. --- github/github-accessors.go | 208 +++++++++++++++++++++++++++++ github/github-accessors_test.go | 229 ++++++++++++++++++++++++++++++++ 2 files changed, 437 insertions(+) diff --git a/github/github-accessors.go b/github/github-accessors.go index be3920a5df3..971f1fa6c83 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -862,6 +862,214 @@ func (a *AdvisoryVulnerability) GetVulnerableVersionRange() string { return *a.VulnerableVersionRange } +// GetDiscountAmount returns the DiscountAmount field. +func (a *AICreditUsageItem) GetDiscountAmount() float64 { + if a == nil { + return 0 + } + return a.DiscountAmount +} + +// GetDiscountQuantity returns the DiscountQuantity field. +func (a *AICreditUsageItem) GetDiscountQuantity() float64 { + if a == nil { + return 0 + } + return a.DiscountQuantity +} + +// GetGrossAmount returns the GrossAmount field. +func (a *AICreditUsageItem) GetGrossAmount() float64 { + if a == nil { + return 0 + } + return a.GrossAmount +} + +// GetGrossQuantity returns the GrossQuantity field. +func (a *AICreditUsageItem) GetGrossQuantity() float64 { + if a == nil { + return 0 + } + return a.GrossQuantity +} + +// GetModel returns the Model field. +func (a *AICreditUsageItem) GetModel() string { + if a == nil { + return "" + } + return a.Model +} + +// GetNetAmount returns the NetAmount field. +func (a *AICreditUsageItem) GetNetAmount() float64 { + if a == nil { + return 0 + } + return a.NetAmount +} + +// GetNetQuantity returns the NetQuantity field. +func (a *AICreditUsageItem) GetNetQuantity() float64 { + if a == nil { + return 0 + } + return a.NetQuantity +} + +// GetPricePerUnit returns the PricePerUnit field. +func (a *AICreditUsageItem) GetPricePerUnit() float64 { + if a == nil { + return 0 + } + return a.PricePerUnit +} + +// GetProduct returns the Product field. +func (a *AICreditUsageItem) GetProduct() string { + if a == nil { + return "" + } + return a.Product +} + +// GetSKU returns the SKU field. +func (a *AICreditUsageItem) GetSKU() string { + if a == nil { + return "" + } + return a.SKU +} + +// GetUnitType returns the UnitType field. +func (a *AICreditUsageItem) GetUnitType() string { + if a == nil { + return "" + } + return a.UnitType +} + +// GetModel returns the Model field if it's non-nil, zero value otherwise. +func (a *AICreditUsageReport) GetModel() string { + if a == nil || a.Model == nil { + return "" + } + return *a.Model +} + +// GetOrganization returns the Organization field if it's non-nil, zero value otherwise. +func (a *AICreditUsageReport) GetOrganization() string { + if a == nil || a.Organization == nil { + return "" + } + return *a.Organization +} + +// GetProduct returns the Product field if it's non-nil, zero value otherwise. +func (a *AICreditUsageReport) GetProduct() string { + if a == nil || a.Product == nil { + return "" + } + return *a.Product +} + +// GetTimePeriod returns the TimePeriod field. +func (a *AICreditUsageReport) GetTimePeriod() AICreditUsageTimePeriod { + if a == nil { + return AICreditUsageTimePeriod{} + } + return a.TimePeriod +} + +// GetUsageItems returns the UsageItems slice if it's non-nil, nil otherwise. +func (a *AICreditUsageReport) GetUsageItems() []*AICreditUsageItem { + if a == nil || a.UsageItems == nil { + return nil + } + return a.UsageItems +} + +// GetUser returns the User field if it's non-nil, zero value otherwise. +func (a *AICreditUsageReport) GetUser() string { + if a == nil || a.User == nil { + return "" + } + return *a.User +} + +// GetDay returns the Day field. +func (a *AICreditUsageReportOptions) GetDay() int { + if a == nil { + return 0 + } + return a.Day +} + +// GetModel returns the Model field. +func (a *AICreditUsageReportOptions) GetModel() string { + if a == nil { + return "" + } + return a.Model +} + +// GetMonth returns the Month field. +func (a *AICreditUsageReportOptions) GetMonth() int { + if a == nil { + return 0 + } + return a.Month +} + +// GetProduct returns the Product field. +func (a *AICreditUsageReportOptions) GetProduct() string { + if a == nil { + return "" + } + return a.Product +} + +// GetUser returns the User field. +func (a *AICreditUsageReportOptions) GetUser() string { + if a == nil { + return "" + } + return a.User +} + +// GetYear returns the Year field. +func (a *AICreditUsageReportOptions) GetYear() int { + if a == nil { + return 0 + } + return a.Year +} + +// GetDay returns the Day field if it's non-nil, zero value otherwise. +func (a *AICreditUsageTimePeriod) GetDay() int { + if a == nil || a.Day == nil { + return 0 + } + return *a.Day +} + +// GetMonth returns the Month field if it's non-nil, zero value otherwise. +func (a *AICreditUsageTimePeriod) GetMonth() int { + if a == nil || a.Month == nil { + return 0 + } + return *a.Month +} + +// GetYear returns the Year field. +func (a *AICreditUsageTimePeriod) GetYear() int { + if a == nil { + return 0 + } + return a.Year +} + // GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. func (a *Alert) GetClosedAt() Timestamp { if a == nil || a.ClosedAt == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 56417cc6282..46dc44aebaa 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -1067,6 +1067,235 @@ func TestAdvisoryVulnerability_GetVulnerableVersionRange(tt *testing.T) { a.GetVulnerableVersionRange() } +func TestAICreditUsageItem_GetDiscountAmount(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageItem{} + a.GetDiscountAmount() + a = nil + a.GetDiscountAmount() +} + +func TestAICreditUsageItem_GetDiscountQuantity(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageItem{} + a.GetDiscountQuantity() + a = nil + a.GetDiscountQuantity() +} + +func TestAICreditUsageItem_GetGrossAmount(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageItem{} + a.GetGrossAmount() + a = nil + a.GetGrossAmount() +} + +func TestAICreditUsageItem_GetGrossQuantity(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageItem{} + a.GetGrossQuantity() + a = nil + a.GetGrossQuantity() +} + +func TestAICreditUsageItem_GetModel(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageItem{} + a.GetModel() + a = nil + a.GetModel() +} + +func TestAICreditUsageItem_GetNetAmount(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageItem{} + a.GetNetAmount() + a = nil + a.GetNetAmount() +} + +func TestAICreditUsageItem_GetNetQuantity(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageItem{} + a.GetNetQuantity() + a = nil + a.GetNetQuantity() +} + +func TestAICreditUsageItem_GetPricePerUnit(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageItem{} + a.GetPricePerUnit() + a = nil + a.GetPricePerUnit() +} + +func TestAICreditUsageItem_GetProduct(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageItem{} + a.GetProduct() + a = nil + a.GetProduct() +} + +func TestAICreditUsageItem_GetSKU(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageItem{} + a.GetSKU() + a = nil + a.GetSKU() +} + +func TestAICreditUsageItem_GetUnitType(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageItem{} + a.GetUnitType() + a = nil + a.GetUnitType() +} + +func TestAICreditUsageReport_GetModel(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AICreditUsageReport{Model: &zeroValue} + a.GetModel() + a = &AICreditUsageReport{} + a.GetModel() + a = nil + a.GetModel() +} + +func TestAICreditUsageReport_GetOrganization(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AICreditUsageReport{Organization: &zeroValue} + a.GetOrganization() + a = &AICreditUsageReport{} + a.GetOrganization() + a = nil + a.GetOrganization() +} + +func TestAICreditUsageReport_GetProduct(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AICreditUsageReport{Product: &zeroValue} + a.GetProduct() + a = &AICreditUsageReport{} + a.GetProduct() + a = nil + a.GetProduct() +} + +func TestAICreditUsageReport_GetTimePeriod(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageReport{} + a.GetTimePeriod() + a = nil + a.GetTimePeriod() +} + +func TestAICreditUsageReport_GetUsageItems(tt *testing.T) { + tt.Parallel() + zeroValue := []*AICreditUsageItem{} + a := &AICreditUsageReport{UsageItems: zeroValue} + a.GetUsageItems() + a = &AICreditUsageReport{} + a.GetUsageItems() + a = nil + a.GetUsageItems() +} + +func TestAICreditUsageReport_GetUser(tt *testing.T) { + tt.Parallel() + var zeroValue string + a := &AICreditUsageReport{User: &zeroValue} + a.GetUser() + a = &AICreditUsageReport{} + a.GetUser() + a = nil + a.GetUser() +} + +func TestAICreditUsageReportOptions_GetDay(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageReportOptions{} + a.GetDay() + a = nil + a.GetDay() +} + +func TestAICreditUsageReportOptions_GetModel(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageReportOptions{} + a.GetModel() + a = nil + a.GetModel() +} + +func TestAICreditUsageReportOptions_GetMonth(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageReportOptions{} + a.GetMonth() + a = nil + a.GetMonth() +} + +func TestAICreditUsageReportOptions_GetProduct(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageReportOptions{} + a.GetProduct() + a = nil + a.GetProduct() +} + +func TestAICreditUsageReportOptions_GetUser(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageReportOptions{} + a.GetUser() + a = nil + a.GetUser() +} + +func TestAICreditUsageReportOptions_GetYear(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageReportOptions{} + a.GetYear() + a = nil + a.GetYear() +} + +func TestAICreditUsageTimePeriod_GetDay(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &AICreditUsageTimePeriod{Day: &zeroValue} + a.GetDay() + a = &AICreditUsageTimePeriod{} + a.GetDay() + a = nil + a.GetDay() +} + +func TestAICreditUsageTimePeriod_GetMonth(tt *testing.T) { + tt.Parallel() + var zeroValue int + a := &AICreditUsageTimePeriod{Month: &zeroValue} + a.GetMonth() + a = &AICreditUsageTimePeriod{} + a.GetMonth() + a = nil + a.GetMonth() +} + +func TestAICreditUsageTimePeriod_GetYear(tt *testing.T) { + tt.Parallel() + a := &AICreditUsageTimePeriod{} + a.GetYear() + a = nil + a.GetYear() +} + func TestAlert_GetClosedAt(tt *testing.T) { tt.Parallel() var zeroValue Timestamp